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

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19.  
  20. //
  21. // Public interface and shared subsystem data.
  22. //
  23.  
  24. #ifdef EDITOR
  25.  
  26. #include "editor.h"
  27. #include "libimg.h"
  28. #include "intl_csi.h"
  29. #include "xp_str.h"
  30.  
  31. #ifdef USE_SCRIPT
  32. #define  EDT_IS_SCRIPT(tf) (0 != (tf & (TF_SERVER | TF_SCRIPT | TF_STYLE)))
  33. #else
  34. #define EDT_IS_SCRIPT(tf) (FALSE)
  35. #endif
  36.  
  37. //-----------------------------------------------------------------------------
  38. // CEditElement
  39. //-----------------------------------------------------------------------------
  40.  
  41. //
  42. // This version of the constructor is used to create a child element.
  43. //
  44. CEditElement::CEditElement(CEditElement *pParent, TagType tagType, char* pData): 
  45.         m_tagType(tagType), 
  46.         m_pParent(pParent), 
  47.         m_pNext(0), 
  48.         m_pChild(0),
  49.         m_pTagData(0)
  50. {
  51.     if ( pData ) {
  52.         SetTagData(pData);
  53.     }
  54.     CommonConstructor();
  55. }
  56.  
  57. CEditElement::CEditElement(CEditElement *pParent, PA_Tag *pTag, int16 /*csid*/) :
  58.         m_tagType(pTag->type), 
  59.         m_pParent(pParent), 
  60.         m_pNext(0), 
  61.         m_pChild(0),
  62.         m_pTagData(0)
  63. {
  64.     char *locked_buff;
  65.  
  66.     PA_LOCK(locked_buff, char *, pTag->data);
  67.     if( locked_buff && *locked_buff != '>'){
  68.         SetTagData( locked_buff );
  69.     }
  70.     PA_UNLOCK(pTag->data);
  71.     CommonConstructor();
  72. }
  73.  
  74. void CEditElement::CommonConstructor(){
  75.     if( m_pParent ){
  76.         CEditElement* pE = m_pParent->GetChild();
  77.  
  78.         //
  79.         // there already is a child, add this child to the end of the list.
  80.         //
  81.         if( pE ){
  82.             while( pE->m_pNext != 0 ){
  83.                 pE = pE->m_pNext;
  84.             }
  85.             pE->RawSetNextSibling(this);
  86.         }
  87.         else {
  88.             // make this the first child.
  89.             m_pParent->RawSetChild(this);
  90.         }
  91.     }
  92. }
  93.  
  94.  
  95. CEditElement::CEditElement( IStreamIn *pIn, CEditBuffer* /* pBuffer */ ):
  96.         m_pParent(0), 
  97.         m_pNext(0), 
  98.         m_pChild(0),
  99.         m_pTagData(0)
  100. {
  101.     m_tagType = (TagType) pIn->ReadInt();
  102.     m_pTagData = pIn->ReadZString();
  103. }
  104.  
  105. CEditElement::~CEditElement(){
  106.     Finalize();
  107. }
  108.  
  109. void CEditElement::Finalize(){
  110.     Unlink();
  111.     DeleteChildren();
  112.     if( m_pTagData ) {
  113.         XP_FREE(m_pTagData);
  114.         m_pTagData = NULL;
  115.     }
  116. }
  117.  
  118. CEditTextElement* CEditElement::Text(){
  119.     XP_ASSERT(IsText());
  120.     return (CEditTextElement*)this;
  121. }
  122.  
  123. XP_Bool CEditElement::IsLeaf() { return FALSE; }
  124. CEditLeafElement* CEditElement::Leaf(){ XP_ASSERT(IsLeaf());
  125.     return (CEditLeafElement*)this;
  126. }
  127.  
  128. XP_Bool CEditElement::IsRoot() { return FALSE; }
  129. CEditRootDocElement* CEditElement::Root(){ XP_ASSERT(IsRoot());
  130.     return (CEditRootDocElement*)this;
  131. }
  132.  
  133. XP_Bool CEditElement::IsContainer() { return FALSE; }
  134. CEditContainerElement* CEditElement::Container(){ XP_ASSERT(IsContainer());
  135.     return (CEditContainerElement*)this;
  136. }
  137.  
  138. XP_Bool CEditElement::IsList() { return FALSE; }
  139. CEditListElement* CEditElement::List(){ XP_ASSERT(IsList());
  140.     return (CEditListElement*)this;
  141. }
  142.  
  143. XP_Bool CEditElement::IsBreak() { return FALSE; }
  144. CEditBreakElement* CEditElement::Break(){ XP_ASSERT(IsBreak());
  145.     return (CEditBreakElement*)this;
  146. }
  147.  
  148. XP_Bool CEditElement::CausesBreakBefore() { return FALSE;}
  149. XP_Bool CEditElement::CausesBreakAfter() { return FALSE;}
  150. XP_Bool CEditElement::AllowBothSidesOfGap() { return FALSE; }
  151.  
  152. XP_Bool CEditElement::IsImage() { return FALSE; }
  153. CEditImageElement* CEditElement::Image(){
  154.     // Not all P_IMAGE elements are images.  CEditIconElements have P_IMAGE
  155.     //  as their tagType but are not CEditImageElements.
  156.     XP_ASSERT(m_tagType==P_IMAGE
  157.         && GetElementType() == eImageElement
  158.         && IsImage() ) ;
  159.     return (CEditImageElement*)this;
  160. }
  161.  
  162. XP_Bool CEditElement::IsIcon() { return FALSE; }
  163. CEditIconElement* CEditElement::Icon(){ XP_ASSERT(IsIcon());
  164.     return (CEditIconElement*)this;
  165. }
  166.  
  167. CEditTargetElement* CEditElement::Target(){ XP_ASSERT(GetElementType() == eTargetElement);
  168.     return (CEditTargetElement*)this;
  169. }
  170.  
  171. CEditHorizRuleElement* CEditElement::HorizRule(){ XP_ASSERT(m_tagType==P_HRULE);
  172.     return (CEditHorizRuleElement*)this;
  173. }
  174.  
  175. XP_Bool CEditElement::IsRootDoc() { return FALSE; }
  176. CEditRootDocElement* CEditElement::RootDoc() { XP_ASSERT(IsRootDoc()); return (CEditRootDocElement*) this; }
  177. XP_Bool CEditElement::IsSubDoc() { return FALSE; }
  178. CEditSubDocElement* CEditElement::SubDoc() { XP_ASSERT(IsSubDoc()); return (CEditSubDocElement*) this; }
  179. XP_Bool CEditElement::IsTable() { return FALSE; }
  180. CEditTableElement* CEditElement::Table() { XP_ASSERT(IsTable()); return (CEditTableElement*) this; }
  181. XP_Bool CEditElement::IsTableRow() { return FALSE; }
  182. CEditTableRowElement* CEditElement::TableRow() { XP_ASSERT(IsTableRow()); return (CEditTableRowElement*) this; }
  183. XP_Bool CEditElement::IsTableCell() { return FALSE; }
  184. CEditTableCellElement* CEditElement::TableCell() { XP_ASSERT(IsTableCell()); return (CEditTableCellElement*) this; }
  185. XP_Bool CEditElement::IsCaption() { return FALSE; }
  186. CEditCaptionElement* CEditElement::Caption() { XP_ASSERT(IsCaption()); return (CEditCaptionElement*) this; }
  187. XP_Bool CEditElement::IsText() { return FALSE; }
  188. XP_Bool CEditElement::IsLayer() { return FALSE; }
  189. CEditLayerElement* CEditElement::Layer() { XP_ASSERT(IsLayer()); return (CEditLayerElement*) this; }
  190. XP_Bool CEditElement::IsDivision() { return FALSE; }
  191. CEditDivisionElement* CEditElement::Division() { XP_ASSERT(IsDivision()); return (CEditDivisionElement*) this; }
  192.  
  193. XP_Bool CEditElement::IsEndOfDocument() { return GetElementType() == eEndElement; }
  194. XP_Bool CEditElement::IsEndContainer() { return FALSE; }
  195.  
  196.  
  197. void
  198. CEditElement::SetTagData(char* tagData)
  199. {
  200.     if( m_pTagData ) {
  201.         XP_FREE(m_pTagData);
  202.     }
  203.     if( tagData ){
  204.         m_pTagData = XP_STRDUP(tagData);
  205.     }
  206.     else {
  207.         m_pTagData = tagData;
  208.     }
  209. }
  210.  
  211. void CEditElement::StreamOut( IStreamOut *pOut ){
  212.     pOut->WriteInt( GetElementType() );
  213.     pOut->WriteInt( m_tagType );
  214.     pOut->WriteZString( m_pTagData );
  215. }
  216.  
  217. XP_Bool CEditElement::ShouldStreamSelf( CEditSelection& local, CEditSelection& selection)
  218. {
  219.     return ( local.EqualRange( selection ) || ! local.Contains(selection));
  220. }
  221.  
  222. // Partially stream out this element and each child.except if we are told to stream all
  223. void CEditElement::PartialStreamOut( IStreamOut* pOut, CEditSelection& selection) 
  224. {
  225.     CEditSelection local;
  226.     GetAll(local);
  227. #ifdef DEBUG
  228.     TagType type = GetType();
  229. #endif
  230.  
  231.     if ( local.Intersects(selection) )
  232.     {
  233.         XP_Bool bWriteSelf = ShouldStreamSelf(local, selection);
  234.         if( bWriteSelf )
  235.         {
  236.             StreamOut(pOut);
  237.         }
  238.         CEditElement* pChild;
  239.         for ( pChild = GetChild(); pChild; pChild = pChild->GetNextSibling() )
  240.         {
  241.             pChild->PartialStreamOut(pOut, selection);
  242.         }
  243.         if ( bWriteSelf )
  244.         {
  245.             pOut->WriteInt((int32)eElementNone);
  246.         }
  247.     }
  248. }
  249.  
  250. XP_Bool CEditElement::ClipToMe(CEditSelection& selection, CEditSelection& local) {
  251.     // Returns TRUE if selection intersects with "this".
  252.     GetAll(local);
  253.     return local.ClipTo(selection);
  254. }
  255.  
  256. void CEditElement::GetAll(CEditSelection& selection) {
  257.     CEditLeafElement* pFirstMostChild = CEditLeafElement::Cast(GetFirstMostChild());
  258.     if ( ! pFirstMostChild ) {
  259.         XP_ASSERT(FALSE);
  260.         return;
  261.     }
  262.     selection.m_start.m_pElement = GetFirstMostChild()->Leaf();
  263.     selection.m_start.m_iPos = 0;
  264.     CEditLeafElement* pLast = GetLastMostChild()->Leaf();
  265.     CEditLeafElement* pNext = pLast->NextLeaf();
  266.     if ( pNext ) {
  267.         selection.m_end.m_pElement = pNext;
  268.         selection.m_end.m_iPos = 0;
  269.     }
  270.     else {
  271.      // edge of document. Can't select any further.
  272.         selection.m_end.m_pElement = pLast;
  273.         selection.m_end.m_iPos = pLast->GetLen();
  274.     }
  275.     selection.m_bFromStart = FALSE;
  276. }
  277.  
  278. EEditElementType CEditElement::GetElementType()
  279. {
  280.     return eElement;
  281. }
  282.  
  283. // Get parent table of the element
  284. CEditTableElement* CEditElement::GetParentTable()
  285. {
  286.     CEditElement *pElement = this;
  287.     do { pElement = pElement->GetParent(); }
  288.     while( pElement && !pElement->IsTable() );
  289.     return (CEditTableElement*)pElement;
  290. }
  291.  
  292. // 
  293. // static function calls the appropriate stream constructor
  294. //
  295.  
  296. CEditElement* CEditElement::StreamCtor( IStreamIn *pIn, CEditBuffer *pBuffer ){
  297.     CEditElement* pResult = StreamCtorNoChildren(pIn, pBuffer);
  298.     if ( pResult ) {
  299.         pResult->StreamInChildren(pIn, pBuffer);
  300.     }
  301.     return pResult;
  302. }
  303.  
  304. void CEditElement::StreamInChildren(IStreamIn* pIn, CEditBuffer* pBuffer){
  305.     CEditElement* pChild;
  306.     while ( (pChild = CEditElement::StreamCtor(pIn, pBuffer)) != NULL ) {
  307.         pChild->InsertAsLastChild(this);
  308.     }
  309. }
  310.  
  311. CEditElement* CEditElement::StreamCtorNoChildren( IStreamIn *pIn, CEditBuffer *pBuffer ){
  312.  
  313.     EEditElementType eType = (EEditElementType) pIn->ReadInt();
  314.     switch( eType ){
  315.         case eElementNone:
  316.             return 0;
  317.  
  318.         case eElement:
  319.             return new CEditElement( pIn, pBuffer );
  320.  
  321.         case eTextElement:
  322.             return new CEditTextElement( pIn, pBuffer  );
  323.  
  324.         case eImageElement:
  325.             return new CEditImageElement( pIn, pBuffer  );
  326.  
  327.         case eHorizRuleElement:
  328.             return new CEditHorizRuleElement( pIn, pBuffer  );
  329.  
  330.         case eBreakElement:
  331.             return new CEditBreakElement( pIn, pBuffer  );
  332.  
  333.         case eContainerElement:
  334.             return new CEditContainerElement( pIn, pBuffer  );
  335.  
  336.         case eListElement:
  337.             return new CEditListElement( pIn, pBuffer  );
  338.  
  339.         case eIconElement:
  340.             return new CEditIconElement( pIn, pBuffer  );
  341.  
  342.         case eTargetElement:
  343.             return new CEditTargetElement( pIn, pBuffer  );
  344.  
  345.         case eTableElement:
  346.              return new CEditTableElement( pIn, pBuffer  );
  347.  
  348.         case eCaptionElement:
  349.              return new CEditCaptionElement( pIn, pBuffer  );
  350.  
  351.         case eTableRowElement:
  352.              return new CEditTableRowElement( pIn, pBuffer  );
  353.  
  354.         case eTableCellElement:
  355.              return new CEditTableCellElement( pIn, pBuffer  );
  356.  
  357.         case eLayerElement:
  358.              return new CEditLayerElement( pIn, pBuffer  );
  359.  
  360.         case eDivisionElement:
  361.             return new CEditDivisionElement( pIn, pBuffer );
  362.  
  363.        default:
  364.             XP_ASSERT(0);
  365.     }
  366.     return 0;
  367. }
  368.  
  369.  
  370. //
  371. // Scan up the tree looking to see if we are within 'tagType'.  If we stop and
  372. //  we are not at the top of the tree, we found the tag we are looking for.
  373. //
  374. XP_Bool CEditElement::Within( int tagType ){
  375.     CEditElement* pParent = GetParent();
  376.     while( pParent && pParent->GetType() != tagType ){
  377.         pParent = pParent->GetParent();
  378.     }
  379.     return (pParent != 0);
  380. }
  381.  
  382. CEditBuffer* CEditElement::GetEditBuffer(){
  383.     CEditRootDocElement *pE = GetRootDoc();
  384.     if( pE ){
  385.         return pE->GetBuffer();
  386.     }
  387.     else {
  388.         return 0;
  389.     }
  390. }
  391.  
  392. XP_Bool CEditElement::InFormattedText(){
  393.     CEditElement* pParent = GetParent();
  394.  
  395. #ifdef USE_SCRIPT
  396.     if( IsA( P_TEXT) && (Text()->m_tf & (TF_SERVER|TF_SCRIPT|TF_STYLE)) != 0 ){
  397.         return TRUE;
  398.     }
  399. #endif
  400.  
  401.     while( pParent && BitSet( edt_setFormattedText, pParent->GetType() ) == 0 ){
  402.         pParent = pParent->GetParent();
  403.     }
  404.     return (pParent != 0);
  405. }
  406.  
  407.  
  408. //
  409. // Fills in the data member of the tag, as well as the type informaiton.
  410. //
  411. void CEditElement::SetTagData( PA_Tag* pTag, char* pTagData){
  412.     PA_Block buff;
  413.     char *locked_buff;
  414.     int iLen;
  415.  
  416.     if ( NULL == pTagData ) {
  417.         XP_ASSERT(FALSE);
  418.         return;
  419.     }
  420.  
  421.     pTag->type = m_tagType;
  422.     pTag->edit_element = this;
  423.  
  424.     iLen = XP_STRLEN(pTagData);
  425.     buff = (PA_Block)PA_ALLOC((iLen+1) * sizeof(char));
  426.     if (buff != NULL)
  427.     {
  428.         PA_LOCK(locked_buff, char *, buff);
  429.         XP_BCOPY(pTagData, locked_buff, iLen);
  430.         locked_buff[iLen] = '\0';
  431.         PA_UNLOCK(buff);
  432.     }
  433.     else { 
  434.         // LTNOTE: out of memory, should throw an exception
  435.         return;
  436.     }
  437.  
  438.     pTag->data = buff;
  439.     pTag->data_len = iLen;
  440.     pTag->next = NULL;
  441.     return;
  442. }
  443.  
  444. PA_Tag* CEditElement::TagOpen( int /* iEditOffset */ ){
  445.     PA_Tag *pTag = XP_NEW( PA_Tag );
  446.     XP_BZERO( pTag, sizeof( PA_Tag ) );
  447.     if( GetTagData() ){
  448.         SetTagData( pTag, GetTagData() );
  449.     }
  450.     else {
  451.         SetTagData( pTag, ">" );
  452.     }
  453.     return pTag;
  454. }
  455.  
  456. PA_Tag* CEditElement::TagEnd( ){
  457.     if( TagHasClose( m_tagType ) || BitSet( edt_setWriteEndTag, m_tagType ) ){
  458.         PA_Tag *pTag = XP_NEW( PA_Tag );
  459.         XP_BZERO( pTag, sizeof( PA_Tag ) );
  460.         pTag->type = m_tagType;
  461.         pTag->is_end = TRUE;
  462.         pTag->edit_element = this;
  463.         return pTag;
  464.     }
  465.     return 0;
  466. }
  467.  
  468.  
  469. XP_Bool CEditElement::Reduce( CEditBuffer* /* pBuffer */ ){
  470.     if( !BitSet( edt_setSoloTags,  GetType()  ) &&  GetChild() == 0 ){
  471.         return TRUE;
  472.     }
  473.     else if( BitSet( edt_setCharFormat,  GetType()  ) ){
  474.         CEditElement *pNext = GetNextSibling();
  475.         if( pNext && pNext->GetType() == GetType() ){
  476.             // FONTs and Anchors need better compares than this.
  477.             Merge(pNext);
  478.  
  479.             // make sure it stays in the tree so it dies a natural death (because
  480.             //  it has no children)
  481.             pNext->InsertAfter(this);
  482.             return FALSE;
  483.         }
  484.     }
  485.     return FALSE;
  486. }
  487.  
  488. int CEditElement::GetDefaultFontSize(){
  489.     TagType t = GetType();
  490.     if( !BitSet( edt_setTextContainer,  t  ) ){
  491.         CEditElement* pCont = FindContainer();
  492.         if( pCont ){
  493.             t = pCont->GetType();
  494.         }
  495.         else {
  496.             return 0;       // no default font size
  497.         }
  498.     }
  499.  
  500.     switch( t ){
  501.         case P_HEADER_1:
  502.             return 6;
  503.         case P_HEADER_2:
  504.             return 5;
  505.         case P_HEADER_3:
  506.             return 4;
  507.         case P_HEADER_4:
  508.             return 3;
  509.         case P_HEADER_5:
  510.             return 2; 
  511.         case P_HEADER_6:
  512.             return 1;
  513.         default:
  514.             return 3;
  515.     }
  516. }
  517.  
  518. CEditInsertPoint CEditElement::IndexToInsertPoint(ElementIndex index, XP_Bool bStickyAfter) {
  519.     if ( index < 0 ) {
  520.         XP_ASSERT(FALSE);
  521.         index = 0;
  522.     }
  523.     CEditElement* pChild;
  524.     CEditElement* pLastChild = NULL;
  525.     ElementIndex childCount = 0;
  526.     // XP_TRACE(("IndexToInsertPoint: 0x%08x (%d) index = %d", this, GetElementIndex(), index));
  527.     for ( pChild = GetChild();
  528.         pChild;
  529.         pChild = pChild->GetNextSibling()) {
  530.         pLastChild = pChild;
  531.         childCount = pChild->GetPersistentCount();
  532.         if ( index < childCount
  533.             || (index == childCount && ! bStickyAfter) ){
  534.             return pChild->IndexToInsertPoint(index, bStickyAfter);
  535.         }
  536.             
  537.         index -= childCount;
  538.     }
  539.     if ( ! pLastChild ) { // No children at all
  540.         childCount = GetPersistentCount();
  541.         if ( index > childCount ){
  542.             XP_ASSERT(FALSE);
  543.             index = childCount;
  544.         }
  545.         return CEditInsertPoint(this, index);
  546.     }
  547.     // Ran off end of children
  548.     return pLastChild->IndexToInsertPoint(childCount, bStickyAfter);
  549. }
  550.  
  551. CPersistentEditInsertPoint CEditElement::GetPersistentInsertPoint(ElementOffset offset){
  552.     XP_ASSERT(FALSE);   // This method should never be called
  553.     return CPersistentEditInsertPoint(GetElementIndex() + offset);
  554. }
  555.  
  556. ElementIndex CEditElement::GetPersistentCount()
  557. {
  558.     ElementIndex count = 0;
  559.     for ( CEditElement* c = GetChild();
  560.         c;
  561.         c = c->GetNextSibling() ) {
  562.         count += c->GetPersistentCount();
  563.     }
  564.     return count;
  565. }
  566.  
  567. ElementIndex CEditElement::GetElementIndex()
  568. {
  569.     CEditElement* parent = GetParent();
  570.     if ( parent )
  571.         return parent->GetElementIndexOf(this);
  572.     else
  573.         return 0;
  574. }
  575.  
  576. ElementIndex CEditElement::GetElementIndexOf(CEditElement* child)
  577. {
  578.     ElementIndex index = GetElementIndex();
  579.     for ( CEditElement* c = GetChild();
  580.         c;
  581.         c = c->GetNextSibling() ) {
  582.         if ( child == c )
  583.         {
  584.             return index;
  585.         }
  586.         index += c->GetPersistentCount();
  587.     }
  588.     XP_ASSERT(FALSE); // Couldn't find this child.
  589.     return index;
  590. }
  591.  
  592. void CEditElement::FinishedLoad( CEditBuffer* pBuffer ){
  593.     CEditElement* pNext = 0;
  594.     for ( CEditElement* pChild = GetChild();
  595.         pChild;
  596.         pChild = pNext ) {
  597.         pNext = pChild->GetNextSibling();
  598.         if ( IsAcceptableChild(*pChild) ){
  599.             pChild->FinishedLoad(pBuffer);
  600.         }
  601.         else {
  602. #ifdef DEBUG
  603.             XP_TRACE(("Removing an unacceptable child. Parent type %d child type %d.\n",
  604.                 GetElementType(), pChild->GetElementType()));
  605. #endif
  606.             delete pChild;
  607.         }
  608.     }
  609. }
  610.  
  611. //
  612. // Containers can't be deleted during adjustment or we will blow up.  We need
  613. //  While adjusting containers, new containers are inserted.
  614. //
  615. void CEditElement::AdjustContainers( CEditBuffer* pBuffer ){
  616.     for ( CEditElement* pChild = GetChild();
  617.         pChild;
  618.         pChild = pChild->GetNextSibling() ) {
  619.  
  620.         pChild->AdjustContainers(pBuffer);
  621.     }
  622. }
  623.  
  624.  
  625.  
  626. int16 CEditElement::GetWinCSID(){
  627.     // International Character set ID
  628.     CEditRootDocElement* pRoot = GetRootDoc();
  629.     if ( pRoot ) {
  630.         CEditBuffer* pBuffer = pRoot->GetEditBuffer();
  631.         if ( pBuffer ) {
  632.             return pBuffer->GetRAMCharSetID();
  633.         }
  634.     }
  635.     XP_ASSERT(FALSE);
  636.     return  CS_FE_ASCII;
  637. }
  638.  
  639. void CEditElement::EnsureSelectableSiblings(CEditBuffer* pBuffer)
  640. {
  641.     // To ensure that we can edit before or after the table,
  642.     // make sure that there is a container before and after the table.
  643.  
  644.     CEditElement* pParent = GetParent();
  645.     if ( ! pParent ) {
  646.         return;
  647.     }
  648.     {
  649.         // Make sure the previous sibling exists and is a container
  650.         CEditElement* pPrevious = GetPreviousSibling();
  651.         if ( ! pPrevious || !pPrevious->IsContainer() ) {
  652.             pPrevious = CEditContainerElement::NewDefaultContainer( NULL,
  653.                             pParent->GetDefaultAlignment() );
  654.             pPrevious->InsertBefore(this);
  655.             pPrevious->FinishedLoad(pBuffer);
  656.         }
  657.     }
  658.     {
  659.         // Make sure the next sibling exists and is container
  660.         CEditElement* pNext = GetNextSibling();
  661.         if ( ! pNext || pNext->IsEndContainer() || !pNext->IsContainer() ) {
  662.             pNext = CEditContainerElement::NewDefaultContainer( NULL,
  663.                             pParent->GetDefaultAlignment() );
  664.             pNext->InsertAfter(this);
  665.             pNext->FinishedLoad(pBuffer);
  666.         }
  667.     }
  668. }
  669.  
  670. //-----------------------------------------------------------------------------
  671. //  Reverse navagation (left)
  672. //-----------------------------------------------------------------------------
  673.  
  674. // these routines are a little expensive.  If we need to, we can make the linked
  675. //  lists of elements, doubly linked.
  676. //
  677. CEditElement* CEditElement::GetPreviousSibling(){
  678.     if( GetParent() == 0 ){
  679.         return 0;
  680.     }
  681.  
  682.     // point to our first sibling.
  683.     CEditElement *pSibling = GetParent()->GetChild();
  684.  
  685.     // if we are the first sibling, then there is no previous sibling.
  686.     if ( pSibling == this ){
  687.         return 0;
  688.     }
  689.  
  690.     // if we get an Exception in this loop, the tree is messed up!
  691.     while( pSibling->GetNextSibling() != this ){
  692.         pSibling = pSibling->GetNextSibling();
  693.     }
  694.     return pSibling;
  695.     
  696. }
  697.  
  698. void CEditElement::SetChild(CEditElement *pChild){
  699.     RawSetChild(pChild);
  700. }
  701.  
  702. void CEditElement::SetNextSibling(CEditElement* pNext){
  703.     RawSetNextSibling(pNext);
  704. }
  705.  
  706. CEditElement* CEditElement::GetLastChild(){
  707.     CEditElement* pChild;
  708.     if( (pChild = GetChild()) == 0 ){
  709.         return 0;
  710.     }
  711.     while( pChild->GetNextSibling() ){
  712.         pChild = pChild->GetNextSibling();
  713.     }
  714.     return pChild;
  715. }
  716.  
  717. CEditElement* CEditElement::GetFirstMostChild(){
  718.     CEditElement* pChild = this;
  719.     CEditElement* pPrev = pChild;
  720.     while( pPrev ){
  721.         pChild = pPrev;
  722.         pPrev = pPrev->GetChild();
  723.     }
  724.     return pChild;
  725. }
  726.  
  727. CEditElement* CEditElement::GetLastMostChild(){
  728.     CEditElement* pChild = this;
  729.     CEditElement* pNext = pChild;
  730.     while( pNext ){
  731.         pChild = pNext;
  732.         pNext = pNext->GetLastChild();
  733.     }
  734.     return pChild;
  735. }
  736.  
  737. CEditContainerElement*  CEditElement::GetPreviousNonEmptyContainer()
  738. {
  739.     CEditElement *pPrev = GetPreviousSibling();
  740.     CEditContainerElement* pPrevContainer;
  741.     while (pPrev && pPrev->IsContainer())
  742.     {
  743.         pPrevContainer=pPrev->Container();
  744.         if (!pPrevContainer->IsEmpty())
  745.             return pPrevContainer;
  746.         pPrev=pPrev->GetPreviousSibling();
  747.     }
  748.     return NULL;
  749. }
  750.  
  751. CEditContainerElement*  CEditElement::GetNextNonEmptyContainer()
  752. {
  753.     CEditElement *pNext = GetNextSibling();
  754.     CEditContainerElement* pNextContainer;
  755.     while (pNext&& pNext->IsContainer())
  756.     {
  757.         pNextContainer=pNext->Container();
  758.         if (!pNextContainer->IsEmpty())
  759.             return pNextContainer;
  760.         pNext=pNext->GetNextSibling();
  761.     }
  762.     return NULL;
  763. }
  764.  
  765. ///////////////////////////////////////////////
  766. /////END CEDITELEMENT IMPLEMENTATION///////////
  767. ///////////////////////////////////////////////
  768.  
  769.  
  770. CEditTableCellElement* CEditElement::GetTableCell() {
  771.     // Returns containing cell, or NULL if none.
  772.     CEditElement* pElement = this;
  773.     while ( pElement ) {
  774.         if ( pElement->IsTableCell() ) {
  775.             break;
  776.         }
  777.         if ( pElement->IsSubDoc() && pElement != this ) {
  778.             return NULL;
  779.         }
  780.         pElement = pElement->GetParent();
  781.     }
  782.     return (CEditTableCellElement*) pElement;
  783. }
  784.  
  785. CEditTableCellElement* CEditElement::GetTableCellIgnoreSubdoc() {
  786.     // Returns containing cell, or NULL if none.
  787.     CEditElement* pElement = this;
  788.     while ( pElement ) {
  789.         if ( pElement->IsTableCell() ) {
  790.             break;
  791.         }
  792.         pElement = pElement->GetParent();
  793.     }
  794.     return (CEditTableCellElement*) pElement;
  795. }
  796.  
  797. CEditTableRowElement* CEditElement::GetTableRow() {
  798.     // Returns containing cell, or NULL if none.
  799.     CEditElement* pElement = this;
  800.     while ( pElement ) {
  801.         if ( pElement->IsTableRow() ) {
  802.             break;
  803.         }
  804.         if ( pElement->IsSubDoc() && pElement != this ) {
  805.             return NULL;
  806.         }
  807.         pElement = pElement->GetParent();
  808.     }
  809.     return (CEditTableRowElement*) pElement;
  810. }
  811.  
  812. CEditTableRowElement* CEditElement::GetTableRowIgnoreSubdoc() {
  813.     // Returns containing cell, or NULL if none.
  814.     CEditElement* pElement = this;
  815.     while ( pElement ) {
  816.         if ( pElement->IsTableRow() ) {
  817.             break;
  818.         }
  819.         pElement = pElement->GetParent();
  820.     }
  821.     return (CEditTableRowElement*) pElement;
  822. }
  823.  
  824. CEditCaptionElement* CEditElement::GetCaption() {
  825.     // Returns containing tavle, or NULL if none.
  826.     CEditElement* pElement = this;
  827.     while ( pElement ) {
  828.         if ( pElement->IsCaption() ) {
  829.             break;
  830.         }
  831.         if ( pElement->IsSubDoc() && pElement != this ) {
  832.             return NULL;
  833.         }
  834.         pElement = pElement->GetParent();
  835.     }
  836.     return (CEditCaptionElement*) pElement;
  837. }
  838.  
  839. CEditCaptionElement* CEditElement::GetCaptionIgnoreSubdoc() {
  840.     // Returns containing table, or NULL if none.
  841.     CEditElement* pElement = this;
  842.     while ( pElement ) {
  843.         if ( pElement->IsCaption() ) {
  844.             break;
  845.         }
  846.         pElement = pElement->GetParent();
  847.     }
  848.     return (CEditCaptionElement*) pElement;
  849. }
  850.  
  851. CEditTableElement* CEditElement::GetTable() {
  852.     // Returns containing table, or NULL if none.
  853.     CEditElement* pElement = this;
  854.     while ( pElement ) {
  855.         if ( pElement->IsTable() ) {
  856.             break;
  857.         }
  858.         if ( pElement->IsSubDoc() && pElement != this ) {
  859.             return NULL;
  860.         }
  861.         pElement = pElement->GetParent();
  862.     }
  863.     return (CEditTableElement*) pElement;
  864. }
  865.  
  866. CEditTableElement* CEditElement::GetTableIgnoreSubdoc() {
  867.     // Returns containing table, or NULL if none.
  868.     CEditElement* pElement = this;
  869.     while ( pElement ) {
  870.         if ( pElement->IsTable() ) {
  871.             break;
  872.         }
  873.         pElement = pElement->GetParent();
  874.     }
  875.     return (CEditTableElement*) pElement;
  876. }
  877.  
  878. CEditElement* CEditElement::GetTopmostTableOrLayer() {
  879.     // Returns containing table, or NULL if none.
  880.     CEditElement* pResult = NULL;
  881.     CEditElement* pElement = this;
  882.     while ( pElement ) {
  883.         if ( pElement->IsTable() || pElement->IsLayer()) {
  884.             pResult = pElement;
  885.         }
  886.         pElement = pElement->GetParent();
  887.     }
  888.     return pResult;
  889. }
  890.  
  891. CEditElement* CEditElement::GetTableOrLayerIgnoreSubdoc() {
  892.     // Returns containing table, or NULL if none.
  893.     CEditElement* pElement = this;
  894.     while ( pElement ) {
  895.         if ( pElement->IsTable() || pElement->IsLayer() ) {
  896.             break;
  897.         }
  898.         pElement = pElement->GetParent();
  899.     }
  900.     return pElement;
  901. }
  902.  
  903. CEditElement* CEditElement::GetSubDocOrLayerSkipRoot() {
  904.     // Returns containing sub-doc, or NULL if none.
  905.     CEditElement* pElement = this;
  906.     while ( pElement ) {
  907.         if ( pElement->IsLayer() ||
  908.             (pElement->IsSubDoc() && !pElement->IsRoot() ) ) {
  909.             break;
  910.         }
  911.         pElement = pElement->GetParent();
  912.     }
  913.     return pElement;
  914. }
  915.  
  916. CEditLayerElement* CEditElement::GetLayer() {
  917.     // Returns containing Layer, or NULL if none.
  918.     CEditElement* pElement = this;
  919.     while ( pElement ) {
  920.         if ( pElement->IsLayer() ) {
  921.             break;
  922.         }
  923.         if ( pElement->IsSubDoc() && pElement != this ) {
  924.             return NULL;
  925.         }
  926.         pElement = pElement->GetParent();
  927.     }
  928.     return (CEditLayerElement*) pElement;
  929. }
  930.  
  931. CEditLayerElement* CEditElement::GetLayerIgnoreSubdoc() {
  932.     // Returns containing Layer, or NULL if none.
  933.     CEditElement* pElement = this;
  934.     while ( pElement ) {
  935.         if ( pElement->IsLayer() ) {
  936.             break;
  937.         }
  938.         pElement = pElement->GetParent();
  939.     }
  940.     return (CEditLayerElement*) pElement;
  941. }
  942.  
  943. CEditSubDocElement* CEditElement::GetSubDoc() {
  944.     // Returns containing sub-doc, or NULL if none.
  945.     CEditElement* pElement = this;
  946.     while ( pElement ) {
  947.         if ( pElement->IsSubDoc() ) {
  948.             break;
  949.         }
  950.         pElement = pElement->GetParent();
  951.     }
  952.     return (CEditSubDocElement*) pElement;
  953. }
  954.  
  955. CEditSubDocElement* CEditElement::GetSubDocSkipRoot() {
  956.     // Returns containing sub-doc, or NULL if none.
  957.     CEditElement* pElement = this;
  958.     while ( pElement ) {
  959.         if ( pElement->IsSubDoc() && !pElement->IsRoot() ) {
  960.             break;
  961.         }
  962.         pElement = pElement->GetParent();
  963.     }
  964.     return (CEditSubDocElement*) pElement;
  965. }
  966.  
  967.  
  968. CEditRootDocElement* CEditElement::GetRootDoc(){
  969.     // Returns containing root.
  970.     CEditElement* pElement = this;
  971.     while ( pElement ) {
  972.         if ( pElement->IsRoot() ) {
  973.             break;
  974.         }
  975.         pElement = pElement->GetParent();
  976.     }
  977.     return (CEditRootDocElement*) pElement;
  978. }
  979.  
  980. XP_Bool CEditElement::InMungableMailQuote(){
  981.     // Returns true if this element is within a mungable mail quote.
  982.     CEditElement* pElement = this;
  983.     while ( pElement ) {
  984.         if ( pElement->IsSubDoc() ) {
  985.             return FALSE;
  986.         }
  987.         else if ( pElement->IsList() ) {
  988.             CEditListElement* pList = pElement->List();
  989.             if ( pList->IsMailQuote() ) {
  990.                 return TRUE;
  991.             }
  992.         }
  993.         pElement = pElement->GetParent();
  994.     }
  995.     return FALSE;
  996. }
  997.  
  998. XP_Bool CEditElement::InMailQuote(){
  999.   // Returns true if this element is within a mail quote.
  1000.   return (GetMailQuote() != NULL);
  1001. }
  1002.  
  1003. CEditListElement* CEditElement::GetMailQuote() {
  1004.     CEditElement* pElement = this;
  1005.     while ( pElement ) {
  1006.         if ( pElement->IsList() ) {
  1007.             CEditListElement* pList = pElement->List();
  1008.             if ( pList->IsMailQuote() ) {
  1009.                 return pList;
  1010.             }
  1011.         }
  1012.         pElement = pElement->GetParent();
  1013.     }
  1014.     return NULL;
  1015. }
  1016.  
  1017. ED_Alignment CEditElement::GetDefaultAlignment(){
  1018.     if ( m_pParent )
  1019.         return m_pParent->GetDefaultAlignment();
  1020.  
  1021.     return ED_ALIGN_DEFAULT;
  1022. }
  1023.  
  1024. ED_Alignment CEditElement::GetDefaultVAlignment(){
  1025.     if ( m_pParent )
  1026.         return m_pParent->GetDefaultVAlignment();
  1027.     return ED_ALIGN_TOP;
  1028. }
  1029.  
  1030. CEditElement* CEditElement::UpLeft( PMF_EditElementTest pmf, void *pTestData ){
  1031.     CEditElement *pPrev = this;
  1032.     CEditElement* pRet;
  1033.  
  1034.     while( (pPrev = pPrev->GetPreviousSibling()) != NULL ){
  1035.         pRet = pPrev->DownLeft( pmf, pTestData );
  1036.         if( pRet ){
  1037.             return pRet;
  1038.         }
  1039.     }
  1040.     if( GetParent() ){
  1041.         return GetParent()->UpLeft( pmf, pTestData );
  1042.     }
  1043.     else{
  1044.         return 0;
  1045.     }
  1046. }
  1047.  
  1048. //
  1049. // All the children come before the node.
  1050. //
  1051. CEditElement* CEditElement::DownLeft( PMF_EditElementTest pmf, void *pTestData, 
  1052.             XP_Bool /* bIgnoreThis */ ){
  1053.     CEditElement *pChild;
  1054.     CEditElement *pRet;
  1055.  
  1056.     pChild = GetLastChild();
  1057.     while( pChild != NULL ){
  1058.         if( (pRet = pChild->DownLeft( pmf, pTestData )) != NULL ){
  1059.             return pRet;
  1060.         }
  1061.         pChild = pChild->GetPreviousSibling();
  1062.     }
  1063.     if( TestIsTrue( pmf, pTestData ) ){ 
  1064.         return this;
  1065.     }
  1066.     return 0;
  1067. }
  1068.  
  1069.  
  1070. CEditElement* CEditElement::FindPreviousElement( PMF_EditElementTest pmf, 
  1071.         void *pTestData ){
  1072.     return UpLeft( pmf, pTestData );
  1073. }
  1074.  
  1075. //-----------------------------------------------------------------------------
  1076. // forward navagation (right)
  1077. //-----------------------------------------------------------------------------
  1078. CEditElement* CEditElement::UpRight( PMF_EditElementTest pmf, void *pTestData ){
  1079.     CEditElement *pNext = this;
  1080.     CEditElement* pRet;
  1081.  
  1082.     while( (pNext = pNext->GetNextSibling()) != NULL ){
  1083.         pRet = pNext->DownRight( pmf, pTestData );
  1084.         if( pRet ){
  1085.             return pRet;
  1086.         }
  1087.     }
  1088.     if( GetParent() ){
  1089.         return GetParent()->UpRight( pmf, pTestData );
  1090.     }
  1091.     else{
  1092.         return 0;
  1093.     }
  1094. }
  1095.  
  1096.  
  1097. CEditElement* CEditElement::DownRight( PMF_EditElementTest pmf, void *pTestData, 
  1098.             XP_Bool bIgnoreThis ){
  1099.  
  1100.     CEditElement *pChild;
  1101.     CEditElement *pRet;
  1102.  
  1103.     if( !bIgnoreThis && TestIsTrue( pmf, pTestData ) ){ 
  1104.         return this;
  1105.     }
  1106.     pChild = GetChild();
  1107.     while( pChild != NULL ){
  1108.         if( (pRet = pChild->DownRight( pmf, pTestData )) != NULL ){
  1109.             return pRet;
  1110.         }
  1111.         pChild = pChild->GetNextSibling();
  1112.     }
  1113.     return 0;
  1114. }
  1115.  
  1116. CEditElement* CEditElement::FindNextElement( PMF_EditElementTest pmf, 
  1117.         void *pTestData ){
  1118.     CEditElement *pRet;
  1119.  
  1120.     pRet = DownRight( pmf, pTestData, TRUE );
  1121.     if( pRet ){
  1122.         return pRet;
  1123.     }
  1124.     return UpRight( pmf, pTestData );
  1125. }
  1126.  
  1127.  
  1128. // 
  1129. // routine looks for a valid text block for positioning during editing.
  1130. //
  1131. XP_Bool CEditElement::FindText( void* /*pVoid*/ ){ 
  1132.     CEditElement *pPrev;
  1133.  
  1134.     //
  1135.     // if this is a text block and the layout element actually points to 
  1136.     //  something, return it.
  1137.     //
  1138.     if( GetType() == P_TEXT ){
  1139.         CEditTextElement *pText = Text();
  1140.         if( pText->GetLen() == 0 ){
  1141.             //
  1142.             // Find only empty text blocks that occur at the beginning of
  1143.             //  a paragraph (as a paragraph place holder)
  1144.             //
  1145.             pPrev = FindPreviousElement( &CEditElement::FindTextAll, 0 );
  1146.             if( pPrev && pPrev->FindContainer() == FindContainer() ){
  1147.                 return FALSE;
  1148.             }
  1149.         }
  1150.         return TRUE;
  1151.     }
  1152.     return FALSE;
  1153. }
  1154.  
  1155. XP_Bool CEditElement::FindImage( void* /*pVoid*/ ){ 
  1156.     return IsImage() ;
  1157. }
  1158.  
  1159. XP_Bool CEditElement::FindTarget( void* /*pVoid*/ ){ 
  1160.     return GetElementType() == eTargetElement ;
  1161. }
  1162.  
  1163. XP_Bool CEditElement::FindUnknownHTML( void* /*pVoid*/ ){ 
  1164.   return IsLeaf() && Leaf()->IsUnknownHTML();
  1165. }
  1166.  
  1167. // 
  1168. // routine looks for a valid text block for positioning during editing.
  1169. //
  1170. XP_Bool CEditElement::FindLeaf( void* pVoid ){ 
  1171.     if( !IsLeaf() ){
  1172.         return FALSE;
  1173.     }
  1174.     if( IsA(P_TEXT) ){
  1175.         return FindText( pVoid );
  1176.     }
  1177.     else {
  1178.         return TRUE;
  1179.     }
  1180. }
  1181.  
  1182.  
  1183. XP_Bool CEditElement::FindTextAll( void* /*pVoid*/ ){ 
  1184.  
  1185.     //
  1186.     // if this is a text block and the layout element actually points to 
  1187.     //  something, return it.
  1188.     //
  1189.     if( GetType() == P_TEXT ){
  1190.         return TRUE;
  1191.     }
  1192.     return FALSE;
  1193. }
  1194.  
  1195. XP_Bool CEditElement::FindLeafAll( void* /*pVoid*/ ){ 
  1196.  
  1197.     //
  1198.     // if this is a text block and the layout element actually points to 
  1199.     //  something, return it.
  1200.     //
  1201.     if( IsLeaf() ){
  1202.         return TRUE;
  1203.     }
  1204.     return FALSE;
  1205. }
  1206.  
  1207. XP_Bool CEditElement::FindTable( void* /*pVoid*/ ){ 
  1208.     return IsTable();
  1209. }
  1210.  
  1211. XP_Bool CEditElement::FindTableRow( void* /*pVoid*/ ){ 
  1212.     return IsTableRow();
  1213. }
  1214.  
  1215. XP_Bool CEditElement::FindTableCell( void* /*pVoid*/ ){ 
  1216.     return IsTableCell();
  1217. }
  1218.  
  1219. XP_Bool CEditElement::SplitContainerTest( void* /*pVoid*/ ){ 
  1220.     return BitSet( edt_setTextContainer,  GetType()  );
  1221. }
  1222.  
  1223. XP_Bool CEditElement::SplitFormattingTest( void* pVoid ){ 
  1224.     return (void*)GetType() == pVoid;
  1225. }
  1226.  
  1227. XP_Bool CEditElement::FindContainer( void* /*pVoid*/ ){
  1228.     return IsContainer();
  1229. }
  1230.  
  1231. XP_Bool CEditElement::GetWidth(XP_Bool * pPercent, int32 * pWidth) {
  1232.     PA_Tag *pTag = TagOpen(0);
  1233.     if( !pTag ){
  1234.         return FALSE;
  1235.     }
  1236.     XP_Bool bPercent;
  1237.     int32   iWidth;
  1238.     XP_Bool bDefined = edt_FetchDimension( pTag, 
  1239.                            PARAM_WIDTH, &iWidth, &bPercent, 0, FALSE, GetWinCSID() );
  1240.     if( bDefined ){
  1241.         if( pPercent ){
  1242.             *pPercent = bPercent;
  1243.         }
  1244.         if( pWidth ){
  1245.             *pWidth = iWidth;
  1246.         }
  1247.     }
  1248.     return bDefined;
  1249. }
  1250.  
  1251. XP_Bool CEditElement::GetHeight(XP_Bool * pPercent, int32 * pHeight) {
  1252.     PA_Tag *pTag = TagOpen(0);
  1253.     if( !pTag ){
  1254.         return FALSE;
  1255.     }
  1256.     XP_Bool bPercent;
  1257.     int32   iHeight;
  1258.     XP_Bool bDefined = edt_FetchDimension( pTag,
  1259.                            PARAM_HEIGHT, &iHeight, &bPercent, 0, FALSE, GetWinCSID() );
  1260.     if( bDefined ){
  1261.         if( pPercent ){
  1262.             *pPercent = bPercent;
  1263.         }
  1264.         if( pHeight ){
  1265.             *pHeight = iHeight;
  1266.         }
  1267.     }
  1268.     return bDefined;
  1269. }
  1270.  
  1271. void CEditElement::SetSize(XP_Bool /* bWidthPercent */, int32 /* iWidth */,
  1272.     XP_Bool /* bHeightPercent */, int32 /* iHeight */){
  1273. }
  1274.  
  1275. XP_Bool CEditElement::CanReflow() {
  1276.     return TRUE;
  1277. }
  1278.  
  1279. //-----------------------------------------------------------------------------
  1280. //  Default printing routines.
  1281. //-----------------------------------------------------------------------------
  1282.  
  1283. void CEditElement::PrintOpen( CPrintState *pPrintState ){
  1284.     InternalPrintOpen(pPrintState, m_pTagData);
  1285. }
  1286.  
  1287. void CEditElement::InternalPrintOpen( CPrintState *pPrintState, char* pTagData ){
  1288.     if( !(BitSet( edt_setCharFormat, GetType())
  1289.         || BitSet( edt_setSuppressNewlineBefore, GetType())) ){
  1290.         pPrintState->m_pOut->Write( "\n", 1 );
  1291.         pPrintState->m_iCharPos = 0;
  1292.     }
  1293.  
  1294.     if( pTagData && *pTagData != '>' ){
  1295.         char *pStr = pTagData;
  1296.         while( *pStr == ' ' ) pStr++;
  1297.         // Trim trailing white-space in-place
  1298.         {
  1299.             intn len = XP_STRLEN(pStr);
  1300.             while ( len > 1 && pStr[len-2] == ' ') {
  1301.                 len--;
  1302.             }
  1303.             if ( len > 1 ) {
  1304.                 pStr[len-1] = '>';
  1305.                 pStr[len] = '\0';
  1306.             }
  1307.         }
  1308.             
  1309.         pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "<%s %s", 
  1310.                 EDT_TagString(GetType()),pStr);
  1311.     }
  1312.     else {
  1313.         pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "<%s>", 
  1314.                 EDT_TagString(GetType()) );
  1315.     }
  1316.  
  1317.     if ( BitSet( edt_setRequireNewlineAfter, GetType()) ){
  1318.         pPrintState->m_pOut->Write( "\n", 1 );
  1319.         pPrintState->m_iCharPos = 0;
  1320.     }
  1321. }
  1322.  
  1323. void CEditElement::PrintEnd( CPrintState *pPrintState ){
  1324.     if( TagHasClose( GetType() ) || BitSet( edt_setWriteEndTag,  GetType()  ) ){
  1325.         pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "</%s>", 
  1326.                 EDT_TagString(GetType()) );
  1327.         if( !BitSet( edt_setCharFormat, GetType() ) ){
  1328.             pPrintState->m_pOut->Write( "\n", 1 );
  1329.             pPrintState->m_iCharPos = 0;
  1330.         }
  1331.     }
  1332. }
  1333.  
  1334.  
  1335. //-----------------------------------------------------------------------------
  1336. // Insertion routines
  1337. //-----------------------------------------------------------------------------
  1338. CEditElement* CEditElement::InsertAfter( CEditElement *pPrev ){
  1339.     XP_ASSERT(m_pParent == NULL);
  1340.     m_pParent = pPrev->GetParent();
  1341.     SetNextSibling(pPrev->GetNextSibling());
  1342.     pPrev->SetNextSibling(this);
  1343.     return m_pParent;
  1344. }
  1345.  
  1346. CEditElement* CEditElement::InsertBefore( CEditElement *pNext ){
  1347.     XP_ASSERT(m_pParent == NULL);
  1348.     XP_ASSERT(pNext != NULL);
  1349.     CEditElement *pPrev = pNext->GetPreviousSibling();
  1350.     if( pPrev == 0 ){
  1351.         InsertAsFirstChild( pNext->GetParent() );
  1352.     }
  1353.     else {
  1354.         m_pParent = pNext->GetParent();
  1355.         SetNextSibling(pPrev->m_pNext);
  1356.         pPrev->SetNextSibling(this);
  1357.     }
  1358.     return m_pParent;
  1359. }
  1360.  
  1361.  
  1362. void CEditElement::InsertAsFirstChild( CEditElement *pParent ){
  1363.     XP_ASSERT(m_pParent == NULL);
  1364.     m_pParent = pParent;
  1365.     SetNextSibling(pParent->GetChild());
  1366.     pParent->SetChild(this);
  1367. }
  1368.  
  1369. void CEditElement::InsertAsLastChild( CEditElement *pParent ){
  1370.     XP_ASSERT(m_pParent == NULL);
  1371.     m_pParent = pParent;
  1372.     SetNextSibling( 0 );
  1373.     CEditElement *pPrev = pParent->GetLastChild();
  1374.     if( pPrev == 0 ){
  1375.         pParent->SetChild(this);
  1376.     }
  1377.     else {
  1378.         pPrev->SetNextSibling(this);
  1379.     }
  1380. }
  1381.  
  1382.  
  1383.  
  1384. CEditElement* CEditElement::Split( CEditElement *pLastChild, 
  1385.             CEditElement* pCloneTree,
  1386.             PMF_EditElementTest pmf,
  1387.             void *pData ){
  1388.  
  1389.     CEditElement *pClone = Clone();
  1390.     pClone->SetChild(pCloneTree);
  1391.  
  1392.     if( pLastChild->m_pNext ){
  1393.         if( pCloneTree != 0 ){
  1394.             pCloneTree->SetNextSibling(pLastChild->m_pNext);
  1395.         }
  1396.         else {
  1397.             pClone->SetChild(pLastChild->m_pNext);
  1398.         }
  1399.         pLastChild->SetNextSibling( 0 );    
  1400.     }
  1401.  
  1402.     //
  1403.     // Reparent all the children that have been moved to the clone.
  1404.     //
  1405.     CEditElement* pNext = pClone->m_pChild;
  1406.     while( pNext ){
  1407.         pNext->m_pParent = pClone;
  1408.         pNext = pNext->GetNextSibling();
  1409.     }
  1410.  
  1411.  
  1412.     //
  1413.     // If we are at the container point
  1414.     //
  1415.     if( TestIsTrue( pmf, pData ) ){
  1416.         return pClone->InsertAfter( this );
  1417.     }
  1418.     else {
  1419.         return GetParent()->Split( this, pClone, pmf, pData );
  1420.     }
  1421.  
  1422. }
  1423.  
  1424. CEditElement* CEditElement::Clone( CEditElement *pParent ){
  1425.     return new CEditElement(pParent, GetType(), GetTagData());
  1426. }
  1427.  
  1428.  
  1429. //
  1430. // Copied formatting, returns the bottom of the chain (Child most formatting
  1431. //  element)
  1432. //
  1433. CEditElement* CEditElement::CloneFormatting( TagType endType ){
  1434.     if( GetType() == endType ){
  1435.         return 0;
  1436.     }
  1437.     else {
  1438.         return Clone( GetParent()->CloneFormatting(endType) );
  1439.     }
  1440. }
  1441.  
  1442. XP_Bool CEditElement::IsFirstInContainer(){
  1443.     CEditElement *pPrevious, *pContainer, *pPreviousContainer;
  1444.  
  1445.     pPrevious = PreviousLeaf();
  1446.     if( pPrevious ){
  1447.         pContainer = FindContainer();
  1448.         pPreviousContainer = pPrevious->FindContainer();
  1449.         if( pContainer != pPreviousContainer ){
  1450.             return TRUE;       
  1451.         }
  1452.         else {
  1453.             return FALSE;
  1454.         }
  1455.     }
  1456.     else {
  1457.         return TRUE;
  1458.     }
  1459. }
  1460.  
  1461. CEditTextElement* CEditElement::PreviousTextInContainer(){
  1462.     CEditElement *pPrevious, *pContainer, *pPreviousContainer;
  1463.     pPrevious = FindPreviousElement( &CEditElement::FindText, 0 );
  1464.     if( pPrevious ){
  1465.         pContainer = FindContainer();
  1466.         pPreviousContainer = pPrevious->FindContainer();
  1467.         if( pContainer != pPreviousContainer ){
  1468.             return 0;
  1469.         }
  1470.         else {
  1471.             return pPrevious->Text();
  1472.         }
  1473.     }
  1474.     else {
  1475.         return 0;
  1476.     }
  1477. }
  1478.  
  1479.  
  1480. CEditTextElement* CEditElement::TextInContainerAfter(){
  1481.     CEditElement *pNext, *pContainer, *pNextContainer;
  1482.  
  1483.     pNext = FindNextElement( &CEditElement::FindText, 0 );
  1484.     if( pNext ){
  1485.         pContainer = FindContainer();
  1486.         pNextContainer = pNext->FindContainer();
  1487.         if( pContainer != pNextContainer ){
  1488.             return 0;       
  1489.         }
  1490.         else {
  1491.             return pNext->Text();
  1492.         }
  1493.     }
  1494.     else {
  1495.         return 0;
  1496.     }
  1497. }
  1498.  
  1499. CEditLeafElement* CEditElement::PreviousLeafInContainer(){
  1500.     CEditElement *pPrevious, *pContainer, *pPreviousContainer;
  1501.     pPrevious = PreviousLeaf();
  1502.     if( pPrevious ){
  1503.         pContainer = FindContainer();
  1504.         pPreviousContainer = pPrevious->FindContainer();
  1505.         if( pContainer != pPreviousContainer ){
  1506.             return 0;
  1507.         }
  1508.         else {
  1509.             return pPrevious->Leaf();
  1510.         }
  1511.     }
  1512.     else {
  1513.         return 0;
  1514.     }
  1515. }
  1516.  
  1517.  
  1518. CEditLeafElement* CEditElement::LeafInContainerAfter(){
  1519.     CEditElement *pNext, *pContainer, *pNextContainer;
  1520.  
  1521.     pNext = NextLeaf();
  1522.     if( pNext ){
  1523.         pContainer = FindContainer();
  1524.         pNextContainer = pNext->FindContainer();
  1525.         if( pContainer != pNextContainer ){
  1526.             return 0;       
  1527.         }
  1528.         else {
  1529.             return pNext->Leaf();
  1530.         }
  1531.     }
  1532.     else {
  1533.         return 0;
  1534.     }
  1535. }
  1536.  
  1537. CEditLeafElement* CEditElement::NextLeafAll(XP_Bool bForward){
  1538.     if ( bForward ) {
  1539.         return (CEditLeafElement*) FindNextElement(&CEditElement::FindLeafAll, 0 );
  1540.     }
  1541.     else {
  1542.         return (CEditLeafElement*) FindPreviousElement(&CEditElement::FindLeafAll, 0 );
  1543.     }
  1544. }
  1545.  
  1546. CEditElement* CEditElement::GetRoot(){
  1547.     CEditElement* pRoot = this;
  1548.     while ( pRoot ) {
  1549.         CEditElement* pParent = pRoot->GetParent();
  1550.         if ( ! pParent ) break;
  1551.         pRoot = pParent;
  1552.     }
  1553.     return pRoot;
  1554. }
  1555.  
  1556. CEditElement* CEditElement::GetCommonAncestor(CEditElement* pOther){
  1557.     if ( this == pOther ) return this;
  1558.  
  1559.     // Start at root, and trickle down to find where they diverge
  1560.  
  1561.     CEditElement* pRoot = GetRoot();
  1562.     CEditElement* pRootOther = pOther->GetRoot();
  1563.     if ( pRoot != pRootOther ) return NULL;
  1564.  
  1565.     CEditElement* pCommon = pRoot;
  1566.  
  1567.     while ( pCommon ){
  1568.         CEditElement* pAncestor = pCommon->GetChildContaining(this);
  1569.         CEditElement* pAncestorOther = pCommon->GetChildContaining(pOther);
  1570.         if ( pAncestor != pAncestorOther ) break;
  1571.         pCommon = pAncestor;
  1572.     }
  1573.     return pCommon;
  1574. }
  1575.  
  1576. CEditElement* CEditElement::GetChildContaining(CEditElement* pDescendant){
  1577.     CEditElement* pParent = pDescendant;
  1578.     while ( pParent ) {
  1579.         CEditElement* pTemp = pParent->GetParent();
  1580.         if ( pTemp == this ) break; // Our direct child
  1581.         pParent = pTemp;
  1582.     }
  1583.     return pParent;
  1584. }
  1585.  
  1586.  
  1587. XP_Bool CEditElement::IsAcceptableChild(CEditElement& /* pChild */) {return TRUE;}
  1588.  
  1589. //
  1590. // Unlink from parent, but keep children
  1591. //
  1592. void CEditElement::Unlink(){
  1593.     CEditElement* pParent = GetParent();
  1594.     if( pParent ){
  1595.         CEditElement *pPrev = GetPreviousSibling();
  1596.         if( pPrev ){
  1597.             pPrev->SetNextSibling( GetNextSibling() );
  1598.         }
  1599.         else {
  1600.             pParent->SetChild(GetNextSibling());
  1601.         }
  1602.         m_pParent = 0;
  1603.         SetNextSibling( 0 );
  1604.     }
  1605.  
  1606. }
  1607.  
  1608. // LTNOTE: 01/01/96
  1609. //  We take take the characteristics of the thing that follows this paragraph
  1610. //  instead of keeping this paragraphs characteristics.
  1611. //
  1612. // jhp -- paste takes characteristic of second, but backspace takes
  1613. // characteristic of first, so we need a flag to control it.
  1614. // bCopyAppendAttributes == TRUE for the second (cut/paste)
  1615. // bCopyAppendAttributes == FALSE for the first (backspace/delete)
  1616.  
  1617. void CEditElement::Merge( CEditElement *pAppendBlock, XP_Bool bCopyAppendAttributes ){
  1618.     CEditElement* pChild = GetLastChild();
  1619.     CEditElement* pAppendChild = pAppendBlock->GetChild();
  1620.  
  1621.     // LTNOTE: 01/01/96 - I don't think this should be happening anymore.
  1622.     //   The way we deal with leaves and containers should keep this from 
  1623.     //   occuring...
  1624.     //
  1625.     // Check to see if pAppendBlock is a child of this.
  1626.     //    
  1627.     // LTNOTE: this is a case where we have
  1628.     // UL: 
  1629.     //   text: some text 
  1630.     //   LI: 
  1631.     //      text: XXX some More text
  1632.     //   LI:
  1633.     //      text: and some more.
  1634.     //
  1635.     // Merge occurs before xxx
  1636.     //  
  1637.     CEditElement *pAppendParent = pAppendBlock->GetParent();
  1638.     CEditElement *pAppendBlock2 = pAppendBlock;
  1639.     XP_Bool bDone = FALSE;
  1640.     while( !bDone && pAppendParent ){
  1641.         if( pAppendParent == this ){
  1642.             pChild = pAppendBlock2->GetPreviousSibling();            
  1643.             bDone = TRUE;
  1644.         }
  1645.         else {
  1646.             pAppendBlock2 = pAppendParent;
  1647.             pAppendParent = pAppendParent->GetParent();
  1648.         }
  1649.     }
  1650.  
  1651.     pAppendBlock->Unlink();
  1652.  
  1653.     if( pChild == 0 ){
  1654.         m_pChild = pAppendChild;
  1655.     }
  1656.     else {
  1657.         CEditElement *pOldNext = pChild->m_pNext;
  1658.         pChild->SetNextSibling( pAppendChild );
  1659.  
  1660.         // kind of sleazy.  we haven't updated the link, GetLastChild will
  1661.         //  return the end of pAppendBlock's children.
  1662.         GetLastChild()->SetNextSibling( pOldNext );
  1663.     }
  1664.     pAppendBlock->m_pChild = 0;
  1665.  
  1666.     while( pAppendChild ){
  1667.         pAppendChild->m_pParent = this;
  1668.         pAppendChild = pAppendChild->GetNextSibling();
  1669.     }
  1670.  
  1671.     //
  1672.     // LTNOTE: whack the container type to reflect the thing we just pulled 
  1673.     //  up. 
  1674.     //
  1675.     //XP_ASSERT( IsContainer() && pAppendBlock->IsContainer() );
  1676.     if( bCopyAppendAttributes && IsContainer() && pAppendBlock->IsContainer() ){
  1677.         Container()->CopyAttributes( pAppendBlock->Container() );
  1678.     }
  1679.  
  1680.     delete pAppendBlock;
  1681. }
  1682.  
  1683. // By default, yes for any non-atomic tag
  1684. XP_Bool CEditElement::IsContainerContainer(){
  1685.     return !BitSet( edt_setNoEndTag, GetType());
  1686. }
  1687.  
  1688. //
  1689. // Search up the tree for the element that contains us.
  1690. // - start with us.
  1691. CEditContainerElement* CEditElement::FindContainer(){
  1692.     CEditElement *pRet = this;
  1693.     while( pRet ){
  1694.         if ( pRet->IsSubDoc() && pRet != this ) break;
  1695.         if( pRet->IsContainer() ){
  1696.             return pRet->Container();
  1697.         }
  1698.         pRet = pRet->GetParent();
  1699.     }
  1700.     return 0;
  1701. }
  1702.  
  1703. //
  1704. // Search up the tree for the element that contains us.
  1705. // - skip us.
  1706.  
  1707. CEditContainerElement* CEditElement::FindEnclosingContainer(){
  1708.     CEditElement *pRet = this->GetParent();
  1709.     if ( pRet ){
  1710.         return pRet->FindContainer();
  1711.     }
  1712.     return 0;
  1713. }
  1714.  
  1715. void CEditElement::FindList( CEditContainerElement*& pContainer, 
  1716.             CEditListElement*& pList ) {
  1717.     
  1718.     pList = 0;
  1719.     XP_Bool bDone = FALSE;
  1720.     pContainer = FindEnclosingContainer();
  1721.     while( !bDone ){
  1722.         if( pContainer->GetParent()->IsList() ){
  1723.             pList = pContainer->GetParent()->List();
  1724.             bDone = TRUE;
  1725.         }
  1726.         else {
  1727.             CEditElement *pParentContainer = pContainer->FindEnclosingContainer();
  1728.             if( pParentContainer ){
  1729.                 pContainer = pParentContainer->Container();
  1730.             }
  1731.             else {
  1732.                 bDone = TRUE;
  1733.             }
  1734.         }
  1735.     }
  1736. }
  1737.  
  1738.  
  1739. CEditElement* CEditElement::FindContainerContainer(){
  1740.     CEditElement *pRet = this;
  1741.     while( pRet ){
  1742.         // Don't need explicit subdoc test because
  1743.         // sub-docs are paragraph containers.
  1744.         if ( pRet->IsContainerContainer() ){
  1745.             return pRet;
  1746.         }
  1747.         pRet = pRet->GetParent();
  1748.     }
  1749.     return 0;
  1750. }
  1751.  
  1752.  
  1753. #ifdef DEBUG
  1754. void CEditElement::ValidateTree(){
  1755.     CEditElement* pChild;
  1756.     CEditElement* pLoopFinder;
  1757.     // Make sure that all of our children point to us.
  1758.     // Makes sure all children are valid.
  1759.     // Make sure we don't have an infinite loop of children.
  1760.     // Use a second pointer that goes
  1761.     // around the loop twice as fast -- if it ever catches up with
  1762.     // pChild, there's a loop. (And yes, since you're wondering,
  1763.     // we did run into infinite loops here...)
  1764.     pChild = GetChild();
  1765.     pLoopFinder = pChild;
  1766.     while( pChild ){
  1767.         if( pChild->GetParent() != this ){
  1768.             XP_ASSERT(FALSE);
  1769.         }
  1770.         XP_ASSERT(IsAcceptableChild(*pChild));
  1771.         pChild->ValidateTree();
  1772.         for ( int i = 0; i < 2; i++ ){
  1773.             if ( pLoopFinder ) {
  1774.                 pLoopFinder = pLoopFinder->GetNextSibling();
  1775.                 if (pLoopFinder == pChild) {
  1776.                     XP_ASSERT(FALSE);
  1777.                     return;
  1778.                 }
  1779.             }
  1780.         }
  1781.         pChild = pChild->GetNextSibling();
  1782.     }
  1783. }
  1784. #endif
  1785.  
  1786. CEditElement* CEditElement::Divide(int /* iOffset */){
  1787.     return this;
  1788. }
  1789.  
  1790. XP_Bool CEditElement::DeleteElement(CEditElement* pTellIfKilled){
  1791.     CEditElement *pKill = this;
  1792.     CEditElement *pParent;
  1793.     XP_Bool bKilled = FALSE;
  1794.     // Delete the table/cell sellection if element is in the list
  1795.     GetEditBuffer()->ClearTableIfContainsElement(pKill);
  1796.     do {
  1797.         pParent = pKill->GetParent();
  1798.         pKill->Unlink();
  1799.         if( pKill == pTellIfKilled ){
  1800.             bKilled = TRUE;
  1801.         }
  1802.         delete pKill;
  1803.         pKill = pParent;
  1804.     } while( pKill->GetChild() == 0 );
  1805.     return bKilled;
  1806. }
  1807.  
  1808. void CEditElement::DeleteChildren(){
  1809.     CEditElement* pNext = 0;
  1810.     for ( CEditElement* pChild = GetChild();
  1811.         pChild;
  1812.         pChild = pNext ) {
  1813.         pNext = pChild->GetNextSibling();
  1814.         pChild->Unlink();
  1815.         pChild->DeleteChildren();
  1816.         delete pChild;
  1817.     }
  1818. }
  1819.  
  1820. // class CEditSubDocElement
  1821.  
  1822. CEditSubDocElement::CEditSubDocElement(CEditElement *pParent, int tagType, char* pData)
  1823.     : CEditElement(pParent, tagType, pData)
  1824. {
  1825. }
  1826.  
  1827. CEditSubDocElement::CEditSubDocElement(CEditElement *pParent, PA_Tag *pTag, int16 csid)
  1828.     : CEditElement(pParent, pTag, csid)
  1829. {
  1830. }
  1831.  
  1832. CEditSubDocElement::CEditSubDocElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer)
  1833.     : CEditElement(pStreamIn, pBuffer)
  1834. {
  1835. }
  1836.  
  1837. void CEditSubDocElement::FinishedLoad( CEditBuffer* pBuffer ){
  1838.     CEditElement* pChild = GetChild();
  1839.     if ( ! pChild ) {
  1840.         // Subdocs have to have children.
  1841.         // Put an empty paragraph into any empty subdoc.
  1842.         pChild = CEditContainerElement::NewDefaultContainer( this,
  1843.                         GetDefaultAlignment() );
  1844.         // Creating it inserts it.
  1845.         (void) new CEditTextElement(pChild, 0);
  1846.     }
  1847.     // Put any leaves into a container.
  1848.     CEditContainerElement* pContainer = NULL;
  1849.     CEditElement* pNext = 0;
  1850.     for ( ;
  1851.         pChild;
  1852.         pChild = pNext) {
  1853.         pNext = pChild->GetNextSibling(); // We might unlink pChild
  1854.         if ( ! IsAcceptableChild(*pChild) ){
  1855.             if ( pChild->IsLeaf() ){
  1856.                 if ( ! pContainer ){
  1857.                     pContainer = CEditContainerElement::NewDefaultContainer(NULL, GetDefaultAlignment());
  1858.                     pContainer->InsertAfter(pChild);
  1859.                 }
  1860.                 pChild->Unlink();
  1861.                 pChild->InsertAsLastChild(pContainer);
  1862.             }
  1863.             else {
  1864.                 XP_TRACE(("CEditSubDocElement: deleteing child of unknown type %d.\n",
  1865.                     pChild->GetElementType()));
  1866.                 delete pChild;
  1867.                 pChild = NULL;
  1868.             }
  1869.         }
  1870.         else {
  1871.             if ( pContainer ){
  1872.                 pContainer->FinishedLoad(pBuffer);
  1873.                 pContainer = NULL;
  1874.             }
  1875.         }
  1876.         if ( pChild ) {
  1877.             pChild->FinishedLoad(pBuffer);
  1878.         }
  1879.     }
  1880.     if ( pContainer ){
  1881.         pContainer->FinishedLoad(pBuffer);
  1882.         pContainer = NULL;
  1883.     }
  1884. }
  1885.  
  1886. XP_Bool CEditSubDocElement::Reduce( CEditBuffer* /* pBuffer */ ){
  1887.     return FALSE;
  1888. }
  1889.  
  1890. // class CEditTableElement
  1891.  
  1892. CEditTableElement::CEditTableElement(intn columns, intn rows)
  1893.     : CEditElement(0, P_TABLE, 0),
  1894.       m_backgroundColor(),
  1895.       m_pBackgroundImage(0),
  1896.       m_iBackgroundSaveIndex(0),
  1897.       m_align(ED_ALIGN_LEFT),
  1898.       m_malign(ED_ALIGN_DEFAULT),
  1899.       m_iWidth(1),
  1900.       m_iWidthPixels(1),
  1901.       m_iHeight(1),
  1902.       m_iHeightPixels(1),
  1903.       m_bWidthPercent(FALSE),
  1904.       m_bHeightPercent(FALSE),
  1905.       m_iInterCellSpace(0),
  1906.       m_iFirstColumnX(0),
  1907.       m_iFirstRowY(0),
  1908.       m_pFirstCellInColumnOrRow(0),
  1909.       m_pNextCellInColumnOrRow(0),
  1910.       m_iGetRow(0),
  1911.       m_iRowY(0),
  1912.       m_iColX(0),
  1913.       m_bSpan(0),
  1914.       m_iRows(0),
  1915.       m_iMaxColumns(0),
  1916.       m_pCurrentCell(0)
  1917. {
  1918.     EDT_TRY {
  1919.         for (intn row = 0; row < rows; row++ ){
  1920.             CEditTableRowElement* pRow = new CEditTableRowElement(columns);
  1921.             pRow->InsertAsFirstChild(this);
  1922.         }
  1923.     }
  1924.     EDT_CATCH_ALL {
  1925.         Finalize();
  1926.         EDT_RETHROW;
  1927.     }
  1928. }
  1929.  
  1930. CEditTableElement::CEditTableElement(CEditElement *pParent, PA_Tag *pTag, int16 csid, ED_Alignment align)
  1931.     : CEditElement(pParent, P_TABLE),
  1932.       m_backgroundColor(),
  1933.       m_pBackgroundImage(0),
  1934.       m_iBackgroundSaveIndex(0),
  1935.       m_align(align),
  1936.       m_malign(ED_ALIGN_DEFAULT),
  1937.       m_iWidth(1),
  1938.       m_iWidthPixels(1),
  1939.       m_iHeight(1),
  1940.       m_iHeightPixels(1),
  1941.       m_bWidthPercent(FALSE),
  1942.       m_bHeightPercent(FALSE),
  1943.       m_iInterCellSpace(0),
  1944.       m_iFirstColumnX(0),
  1945.       m_iFirstRowY(0),
  1946.       m_pFirstCellInColumnOrRow(0),
  1947.       m_pNextCellInColumnOrRow(0),
  1948.       m_iGetRow(0),
  1949.       m_iRowY(0),
  1950.       m_iColX(0),
  1951.       m_bSpan(0),
  1952.       m_iRows(0),
  1953.       m_iMaxColumns(0),
  1954.       m_pCurrentCell(0)
  1955. {
  1956.     switch( m_align ) {
  1957.     case ED_ALIGN_CENTER:
  1958.         /* OK case -- 'center' tag. */
  1959.         m_align = ED_ALIGN_ABSCENTER;
  1960.         break;
  1961.     case ED_ALIGN_LEFT:
  1962.     case ED_ALIGN_ABSCENTER:
  1963.     case ED_ALIGN_RIGHT:
  1964.         break;
  1965.     default:
  1966.         XP_ASSERT(FALSE);
  1967.         /* Falls through */
  1968.     case ED_ALIGN_DEFAULT:
  1969.         m_align = ED_ALIGN_LEFT;
  1970.         break;
  1971.     }
  1972.     if( pTag ){
  1973.         EDT_TableData *pData = ParseParams(pTag, csid);
  1974.         pData->align = m_align;
  1975.         SetData(pData);
  1976.         FreeData(pData);
  1977.     }
  1978. }
  1979.  
  1980. CEditTableElement::CEditTableElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer)
  1981.     : CEditElement(pStreamIn, pBuffer),
  1982.       m_backgroundColor(),
  1983.       m_iBackgroundSaveIndex(0),
  1984.       m_pBackgroundImage(0),
  1985.       m_iWidth(1),
  1986.       m_iWidthPixels(1),
  1987.       m_iHeight(1),
  1988.       m_iHeightPixels(1),
  1989.       m_bWidthPercent(FALSE),
  1990.       m_bHeightPercent(FALSE),
  1991.       m_iInterCellSpace(0),
  1992.       m_iFirstColumnX(0),
  1993.       m_iFirstRowY(0),
  1994.       m_pFirstCellInColumnOrRow(0),
  1995.       m_pNextCellInColumnOrRow(0),
  1996.       m_iGetRow(0),
  1997.       m_iRowY(0),
  1998.       m_iColX(0),
  1999.       m_bSpan(0),
  2000.       m_iRows(0),
  2001.       m_iMaxColumns(0),
  2002.       m_pCurrentCell(0)
  2003. {
  2004.     m_align = (ED_Alignment) pStreamIn->ReadInt();
  2005.     m_malign = (ED_Alignment) pStreamIn->ReadInt();
  2006.  
  2007.     // Get width from stream -- needed for pasting tables/cells
  2008.     // We must get CSID from buffer since element is not in doc yet
  2009.     //TODO: Should we pass in CSID from paste stream instead?
  2010.     PA_Tag* pTag = CEditElement::TagOpen(0);
  2011.     EDT_TableData *pData = ParseParams( pTag, pBuffer->GetRAMCharSetID() );
  2012.     if(pData)
  2013.     {
  2014.         m_iWidth = pData->iWidth;
  2015.         m_bWidthPercent = pData->bWidthPercent;
  2016.         if( !m_bWidthPercent )
  2017.             m_iWidthPixels = m_iWidth;
  2018.  
  2019.         m_iHeight = pData->iHeight;
  2020.         m_bHeightPercent = pData->bHeightPercent;
  2021.         if( !m_bHeightPercent )
  2022.             m_iHeightPixels = m_iHeight;
  2023.  
  2024.         EDT_FreeTableData(pData);
  2025.     }
  2026.     PA_FreeTag( pTag );
  2027. }
  2028.  
  2029. CEditTableElement::~CEditTableElement(){
  2030.    delete m_pBackgroundImage;
  2031. }
  2032.  
  2033. PA_Tag* CEditTableElement::TagOpen( int iEditOffset ){
  2034.     return InternalTagOpen(iEditOffset, FALSE);
  2035. }
  2036.  
  2037. PA_Tag* CEditTableElement::InternalTagOpen( int iEditOffset, XP_Bool bForPrinting){
  2038.     PA_Tag *pRet = 0;
  2039.     PA_Tag* pTag;
  2040.  
  2041.     // create the DIV tag if we need to.
  2042.     if( m_align == ED_ALIGN_ABSCENTER || m_align == ED_ALIGN_RIGHT ){
  2043.         pTag = XP_NEW( PA_Tag );
  2044.         XP_BZERO( pTag, sizeof( PA_Tag ) );
  2045.         if( m_align== ED_ALIGN_RIGHT ){
  2046.             SetTagData( pTag, "ALIGN=right>");
  2047.             pTag->type = P_DIVISION;
  2048.         }
  2049.         else {
  2050.             SetTagData( pTag, ">");
  2051.             pTag->type = P_CENTER;
  2052.         }
  2053.         pRet = pTag;
  2054.     }
  2055.     // create the actual table tag
  2056.  
  2057.     EDT_TableData* pTableData = GetData();
  2058.     char* pTagData = CreateTagData(pTableData, bForPrinting);
  2059.     if ( pTagData ) {
  2060.         pTag = XP_NEW( PA_Tag );
  2061.         XP_BZERO( pTag, sizeof( PA_Tag ) );
  2062.         SetTagData( pTag, pTagData );
  2063.         XP_FREE(pTagData);
  2064.     }
  2065.     else {
  2066.         pTag = CEditElement::TagOpen( iEditOffset );
  2067.     }
  2068.     FreeData(pTableData);
  2069.  
  2070.     // link the tags together.
  2071.     if( pRet == 0 ){
  2072.         pRet = pTag;
  2073.     }
  2074.     else {
  2075.         pRet->next = pTag;
  2076.     }
  2077.     return pRet;
  2078. }
  2079.  
  2080. PA_Tag* CEditTableElement::TagEnd( ){
  2081.     PA_Tag *pRet = CEditElement::TagEnd();
  2082.     if( m_align == ED_ALIGN_ABSCENTER || m_align == ED_ALIGN_RIGHT ){
  2083.         PA_Tag* pTag = XP_NEW( PA_Tag );
  2084.         XP_BZERO( pTag, sizeof( PA_Tag ) );
  2085.         pTag->is_end = TRUE;
  2086.         if( m_align == ED_ALIGN_RIGHT ){
  2087.             pTag->type = P_DIVISION;
  2088.         }
  2089.         else {
  2090.             pTag->type = P_CENTER;
  2091.         }
  2092.  
  2093.         if( pRet == 0 ){
  2094.             pRet = pTag;
  2095.         }
  2096.         else {
  2097.             pRet->next = pTag;
  2098.         }
  2099.     }
  2100.     return pRet;
  2101. }
  2102.  
  2103. CEditTableRowElement* CEditTableElement::GetFirstRow()
  2104. {
  2105.     CEditElement *pChild = GetChild();
  2106.     if( pChild && pChild->IsTableRow() )
  2107.     {
  2108.         return pChild->TableRow();
  2109.     }
  2110.     return NULL;
  2111. }
  2112.  
  2113. CEditTableCellElement* CEditTableElement::GetFirstCell()
  2114. {
  2115.     CEditElement *pChild = GetChild();
  2116.     if( pChild && pChild->IsTableRow() )
  2117.     {
  2118.         pChild = pChild->GetChild();
  2119.         if( pChild && pChild->IsTableCell() )
  2120.         {
  2121.             m_pCurrentCell = pChild->TableCell();
  2122.             return m_pCurrentCell;
  2123.         }
  2124.     }
  2125.     return NULL;
  2126. }
  2127.  
  2128. CEditTableCellElement* CEditTableElement::GetNextCellInTable(intn *pRowCounter) 
  2129.     if( m_pCurrentCell )
  2130.         m_pCurrentCell = m_pCurrentCell->GetNextCellInTable(pRowCounter);
  2131.     return m_pCurrentCell;
  2132. }
  2133.  
  2134. // Total number of rows is reliable, even with COLSPAN and ROWSPAN effects
  2135. intn CEditTableElement::GetRows()
  2136. {
  2137.     m_iRows = 0;
  2138.     for ( CEditElement* pChild = GetChild();
  2139.             pChild;
  2140.                 pChild = pChild->GetNextSibling())
  2141.     {
  2142.         if ( pChild->IsTableRow() )
  2143.         {
  2144.             m_iRows++;
  2145.         }
  2146.     }
  2147.     return m_iRows;
  2148. }
  2149.  
  2150. // Get maximum number of rows in any column,
  2151. //  but doesn't account for effect of ROWSPAN 
  2152. //  on following rows. 
  2153. //  Use GetMaxColumns() for actual number of columns as displayed
  2154. // TODO: THIS MUST BE RESOLVED! WHICH COLUMNS VALUE SHOULD BE USED FOR "COLS=x" TAG?
  2155. intn CEditTableElement::GetColumns()
  2156. {
  2157.     intn maxCols = 0;
  2158.  
  2159.     for ( CEditElement* pChild = GetChild();
  2160.             pChild;
  2161.                 pChild = pChild->GetNextSibling())
  2162.     {
  2163.         if ( pChild->IsTableRow() )
  2164.         {
  2165.             // This now returns accurate value, including COLSPAN effects
  2166.             //   but only after first relayout (depends on accurate m_iColumn)
  2167.             intn cols = pChild->TableRow()->GetCells();
  2168.             if (cols > maxCols)
  2169.             {
  2170.                 maxCols = cols;
  2171.             }
  2172.         }
  2173.     }
  2174.     return maxCols;
  2175. }
  2176.  
  2177. CEditTableRowElement* CEditTableElement::GetRow(int32 Y, intn *pRow)
  2178. {
  2179.     intn iRow = 0;
  2180.     for ( CEditElement* pChild = GetChild();
  2181.             pChild;
  2182.                 pChild = pChild->GetNextSibling())
  2183.     {
  2184.         if ( pChild->IsTableRow() )
  2185.         {
  2186.             // Scan row for first cell that has given Y as top
  2187.             CEditTableCellElement *pCell = (CEditTableCellElement*)(pChild->GetChild());
  2188.             while( pCell && pCell-IsTableCell() )
  2189.             {
  2190.                 if( Y == pCell->GetY()  )
  2191.                 {
  2192.                     if( pRow )
  2193.                         *pRow = iRow;
  2194.                     return pChild->TableRow();
  2195.                 }
  2196.                 pCell = (CEditTableCellElement*)(pCell->GetNextSibling());
  2197.             }
  2198.             iRow++;
  2199.         }
  2200.     }
  2201.     return NULL;
  2202. }
  2203.  
  2204. CEditTableCellElement* CEditTableElement::GetFirstCellInRow(CEditTableCellElement *pCell, XP_Bool bSpan)
  2205. {
  2206.     return GetFirstCellInRow(pCell->GetY(), bSpan);
  2207. }
  2208.  
  2209. // Get first cell of a "real" (geometric) row, based on location
  2210. // If bSpan == TRUE , this will also get a cell that spans given Y, 
  2211. //    even if top of cell != Y
  2212. CEditTableCellElement* CEditTableElement::GetFirstCellInRow(int32 Y, XP_Bool bSpan)
  2213. {
  2214.  
  2215.     CEditTableCellElement *pCell = GetFirstCell();
  2216.     m_iGetRow = 0;
  2217.     m_iRowY = Y;
  2218.     while( pCell )
  2219.     {
  2220.         int32 iCellY = pCell->GetY();
  2221.         if( (bSpan && (Y >= iCellY) && (Y <= (iCellY + pCell->GetHeight()))) ||
  2222.             (!bSpan && ( Y == iCellY)) )
  2223.         {
  2224.             m_pFirstCellInColumnOrRow = m_pNextCellInColumnOrRow = pCell;
  2225.             return pCell;
  2226.         }
  2227.         pCell = pCell->GetNextCellInTable(&m_iGetRow);
  2228.     }
  2229.     return NULL;
  2230. }
  2231.  
  2232. // Get next cell at or spaning the same geometric row as found by GetFirstCellInRow()
  2233. //  or use Y from supplied cell
  2234. CEditTableCellElement* CEditTableElement::GetNextCellInRow(CEditTableCellElement *pCell)
  2235. {
  2236.     if( pCell )
  2237.     {
  2238.         // Initialize using given cell,
  2239.         //  so we don't always have to call GetFirst...
  2240.         m_pNextCellInColumnOrRow = pCell;
  2241.         m_iRowY = pCell->GetY();
  2242.         m_bSpan = FALSE;
  2243.     }
  2244.     pCell = m_pNextCellInColumnOrRow->GetNextCellInTable(&m_iGetRow);
  2245.  
  2246.     while( pCell )
  2247.     {
  2248.         int32 iCellY = pCell->GetY();
  2249.         if( (m_bSpan && (m_iRowY >= iCellY) && (m_iRowY <= (iCellY + pCell->GetHeight()))) ||
  2250.             (!m_bSpan && ( m_iRowY == iCellY)) )
  2251.         {
  2252.             m_pNextCellInColumnOrRow = pCell;
  2253.             return pCell;
  2254.         }
  2255.         pCell = pCell->GetNextCellInTable(&m_iGetRow);
  2256.     }
  2257.     return NULL;
  2258. }
  2259.  
  2260. CEditTableCellElement* CEditTableElement::GetLastCellInRow(CEditTableCellElement *pCell)
  2261. {
  2262.     if( pCell )
  2263.     {
  2264.         // Move until next cell doesn't exist
  2265.         CEditTableCellElement *pNextCell;
  2266.         while( pCell && (pNextCell = GetNextCellInRow(pCell)) != NULL )
  2267.         {
  2268.             pCell = pNextCell;
  2269.         }
  2270.     }
  2271.     return pCell;
  2272. }
  2273.  
  2274. CEditTableCellElement* CEditTableElement::GetPreviousCellInRow(CEditTableCellElement *pCell)
  2275. {
  2276.     if( pCell )
  2277.     {
  2278.         // Start with the first cell..
  2279.         CEditTableCellElement *pFirstCell = GetFirstCellInRow(pCell->GetY());
  2280.         // We are the first cell - no previous exitst
  2281.         if( pFirstCell == pCell )
  2282.             return NULL;
  2283.     
  2284.         // Move until we find cell before us
  2285.         CEditTableCellElement *pPrevCell = pFirstCell;
  2286.         CEditTableCellElement *pNextCell;
  2287.         while( pPrevCell && (pNextCell = GetNextCellInRow(pPrevCell)) != pCell )
  2288.         {
  2289.             pPrevCell = pNextCell;    
  2290.         }
  2291.         return pPrevCell;
  2292.     }
  2293.     return NULL;
  2294. }
  2295.  
  2296. CEditTableCellElement* CEditTableElement::GetFirstCellInColumn(CEditTableCellElement *pCell, XP_Bool bSpan)
  2297. {
  2298.     return GetFirstCellInColumn(pCell->GetX(), bSpan);
  2299. }
  2300.  
  2301. // Get first cell of a "real" (geometric) row, based on location
  2302. // If bSpan is TRUE, this will also get a cell that spans 
  2303. //   given X, even if left of cell != X
  2304. CEditTableCellElement* CEditTableElement::GetFirstCellInColumn(int32 X, XP_Bool bSpan)
  2305. {
  2306.  
  2307.     CEditTableCellElement *pCell = GetFirstCell();
  2308.     m_iGetRow = 0;
  2309.     m_iColX = X;
  2310.     m_bSpan = bSpan;
  2311.     while( pCell )
  2312.     {
  2313.         int32 iCellX = pCell->GetX();
  2314.         if( ( bSpan && (X >= iCellX) && (X <= (iCellX + pCell->GetWidth()))) ||
  2315.             (!bSpan && (X == iCellX)) )
  2316.         {
  2317.             m_pFirstCellInColumnOrRow = m_pNextCellInColumnOrRow = pCell;
  2318.             return pCell;
  2319.         }
  2320.         pCell = pCell->GetNextCellInTable(&m_iGetRow);
  2321.     }
  2322.     return NULL;
  2323. }
  2324.  
  2325. // Get next cell at or spaning the same geometric row as found by GetFirstCellInColumn()
  2326. //  or use X from supplied cell
  2327. CEditTableCellElement* CEditTableElement::GetNextCellInColumn(CEditTableCellElement *pCell)
  2328. {
  2329.     if( pCell )
  2330.     {
  2331.         // Initialize using given cell,
  2332.         //  so we don't always have to call GetFirst...
  2333.         m_pNextCellInColumnOrRow = pCell;
  2334.         m_iColX = pCell->GetX();
  2335.         m_bSpan = FALSE;
  2336.     }
  2337.     pCell = m_pNextCellInColumnOrRow->GetNextCellInTable(&m_iGetRow);
  2338.  
  2339.     while( pCell )
  2340.     {
  2341.         int32 iCellX = pCell->GetX();
  2342.         if( ( m_bSpan && (m_iColX >= iCellX) && (m_iColX <= (iCellX + pCell->GetWidth()))) ||
  2343.             (!m_bSpan && (m_iColX == iCellX)) )
  2344.         {
  2345.             m_pNextCellInColumnOrRow = pCell;
  2346.             return pCell;
  2347.         }
  2348.         pCell = pCell->GetNextCellInTable(&m_iGetRow);
  2349.     }
  2350.     return NULL;
  2351. }
  2352.  
  2353. CEditTableCellElement* CEditTableElement::GetLastCellInColumn(CEditTableCellElement *pCell)
  2354. {
  2355.     if( pCell )
  2356.     {
  2357.         // Move until next cell doesn't exist
  2358.         CEditTableCellElement *pNextCell;
  2359.         while( pCell && (pNextCell = GetNextCellInColumn(pCell)) != NULL )
  2360.         {
  2361.             pCell = pNextCell;
  2362.         }
  2363.     }
  2364.     return pCell;
  2365. }
  2366.  
  2367. CEditTableCellElement* CEditTableElement::GetPreviousCellInColumn(CEditTableCellElement *pCell)
  2368. {
  2369.     if( pCell )
  2370.     {
  2371.         // Start with the first cell..
  2372.         CEditTableCellElement *pFirstCell = GetFirstCellInColumn(pCell->GetX());
  2373.         // We are the first cell - no previous exitst
  2374.         if( pFirstCell == pCell )
  2375.             return NULL;
  2376.     
  2377.         // Move until we find cell before us
  2378.         CEditTableCellElement *pPrevCell = pFirstCell;
  2379.         CEditTableCellElement *pNextCell;
  2380.         while( pPrevCell && (pNextCell = GetNextCellInColumn(pPrevCell)) != pCell )
  2381.         {
  2382.             pPrevCell = pNextCell;    
  2383.         }
  2384.         return pPrevCell;
  2385.     }
  2386.     return NULL;
  2387. }
  2388.  
  2389. // Figure out how many columns are included between given X values
  2390. // Too many COLSPAN and ROWSPAN settings may make this an underestimate
  2391. intn CEditTableElement::GetColumnsSpanned(int32 iStartX, int32 iEndX)
  2392. {
  2393.     if( iStartX == iEndX )
  2394.         return 0;
  2395.  
  2396.     intn iBestGuessColumns = 0;
  2397.     CEditTableRowElement* pRow = GetFirstRow();
  2398.     while( pRow )
  2399.     {
  2400.         intn iColumns = 0;
  2401.         CEditTableCellElement *pCell = pRow->GetFirstCell();
  2402.         while( pCell )
  2403.         {
  2404.             int32 iCellX = pCell->GetX();
  2405.             int32 iWidth = pCell->GetWidth();
  2406.  
  2407.             // Cell is past region - not a good row to use, so quit now
  2408.             if( (iCellX+iWidth) > iEndX )
  2409.                 break;
  2410.  
  2411.             if( iCellX == iStartX )
  2412.             {
  2413.                 if( (iCellX + iWidth) <= iEndX )
  2414.                 {
  2415.                     iColumns += pCell->GetColSpan();
  2416.                     // If this cell exactly spans desired area,
  2417.                     //  then we know the number of columns for sure
  2418.                     if( (iCellX + pCell->GetWidth() + m_iInterCellSpace) == iEndX)
  2419.                     {
  2420.                         return iColumns;
  2421.                     }
  2422.                     // We need to find more cells
  2423.                     //  between current cell and end,
  2424.                     //  so start at next possible cell in this row
  2425.                     iStartX = iCellX + iWidth + m_iInterCellSpace;
  2426.                     // Use what we have so far as our best guess
  2427.                     if( iColumns > iBestGuessColumns )
  2428.                         iBestGuessColumns = iColumns;
  2429.                 }
  2430.             }
  2431.             pCell = pCell->GetNextCellInLogicalRow();
  2432.         }                
  2433.         pRow = pRow->GetNextRow();
  2434.     }
  2435.     return iBestGuessColumns;
  2436. }
  2437.  
  2438. void CEditTableElement::InsertRows(int32 Y, int32 iNewY, intn number, CEditTableElement* pSource)
  2439. {
  2440.     if ( number == 0 )
  2441.     {
  2442.         return; /* A waste of time, but legal. */
  2443.     }
  2444.     intn rows = GetRows();
  2445.     if ( number < 0 || Y < 0 )
  2446.     {
  2447.         /* Illegal. */
  2448.         XP_ASSERT(FALSE);
  2449.         return;
  2450.     }
  2451.     intn iRow;
  2452.     CEditTableRowElement* pRow = GetRow(Y, &iRow);
  2453.     if ( ! pRow ) {
  2454.         XP_ASSERT(FALSE);
  2455.         return;
  2456.     }
  2457.     // We may need this later
  2458.     //CEditTableRowElement* pNextRow = pRow->GetNextRow();
  2459.  
  2460.     // We must examine cells with same geometric row,
  2461.     //  but in different logical rows
  2462.     // So scan all cells near given Y and increase ROWSPAN
  2463.     //  where needed and count actual number of cells 
  2464.     //  to be inserted in new row(s)
  2465.     intn iColumns = 0;
  2466.  
  2467.     CEditTableCellElement *pCell = GetFirstCellInRow(Y, FALSE /*TRUE*/);
  2468.     while( pCell )
  2469.     {
  2470.         int32 iCellY = pCell->GetY();
  2471.         int32 iColSpan = pCell->GetColSpan();
  2472.         int32 iRowSpan = pCell->GetRowSpan();
  2473.         if( Y == iNewY )
  2474.         {
  2475.             // Inserting above
  2476.             
  2477.             if( iCellY == Y )
  2478.             {
  2479.                 // We will be inserting cells above this one
  2480.                 iColumns += iColSpan;
  2481.             } else {
  2482.                 // Cell is actually in row above,
  2483.                 //   so just increase its rowspan
  2484.                 pCell->IncreaseRowSpan(number);
  2485.             }
  2486.         } else {
  2487.             // Inserting below current row
  2488.             if( iNewY == (iCellY + pCell->GetHeight()) )
  2489.             {
  2490.                 // Bottom of this cell is where we want to insert
  2491.                 iColumns += iColSpan;
  2492.             } else {
  2493.                 // Cell spans the new insert Y, so just expand some more
  2494.                 pCell->IncreaseRowSpan(number);
  2495.             }
  2496.         }
  2497.         pCell = GetNextCellInRow();
  2498.     }
  2499.     // Now insert the new rows (including iColumns new cells in each)
  2500.     for ( intn row = 0; row < number; row++ )
  2501.     {
  2502.         CEditTableRowElement* pNewRow;
  2503.         if ( pSource ) {
  2504.             pNewRow = pSource->FindRow(0);
  2505.             pNewRow->Unlink();
  2506.         }
  2507.         else {
  2508.             pNewRow = new CEditTableRowElement(iColumns);
  2509.         }
  2510.         if( Y == iNewY )
  2511.             pNewRow->InsertBefore(pRow);
  2512.         else 
  2513.             pNewRow->InsertAfter(pRow);
  2514.     }
  2515. }
  2516.  
  2517. void CEditTableElement::InsertColumns(int32 X, int32 iNewX, intn number, CEditTableElement* pSource){
  2518.     if ( number == 0 )
  2519.     {
  2520.         return; /* A waste of time, but legal. */
  2521.     }
  2522.     intn rows = GetRows();
  2523.     if ( number < 0 || X < 0 )
  2524.     {
  2525.         /* Illegal. */
  2526.         XP_ASSERT(FALSE);
  2527.         return;
  2528.     }
  2529.  
  2530.     CEditTableRowElement* pRow = GetFirstRow();
  2531.  
  2532.     while( pRow )
  2533.     {
  2534.         CEditTableRowElement* pSourceRow = pSource ? pSource->FindRow(0) : 0;
  2535.         pRow->InsertCells(X, iNewX, number, pSourceRow);
  2536.         //  CURRENTLY, pSource IS NEVER USED
  2537.         //  Was used for UNDO BUFFER, so delete was meant for that
  2538.         if( pSourceRow )
  2539.             delete pSourceRow;
  2540.         
  2541.         pRow = pRow->GetNextRow();
  2542.     }
  2543. }
  2544.  
  2545.  
  2546. // TODO: THIS ISN'T CORRECT YET - DELETING MULTIPLE ROWS IS A REAL PAIN!
  2547. void CEditTableElement::DeleteRows(int32 Y, intn number, CEditTableElement* pUndoContainer)
  2548. {
  2549.     intn i;
  2550.     if ( number == 0 ) {
  2551.         return; /* A waste of time, but legal. */
  2552.     }
  2553.     GetEditBuffer()->ClearTableAndCellSelection();
  2554.     intn rows = GetRows();
  2555.     if ( number < 0 || Y < 0 ){
  2556.         /* Illegal. */
  2557.         XP_ASSERT(FALSE);
  2558.         return;
  2559.     }
  2560.     TXP_GrowableArray_int32 deleted;
  2561.     deleted.SetSize(m_iMaxColumns);
  2562.     for( i = 0; i < m_iMaxColumns; i++ )
  2563.         deleted[i] = 0;
  2564.  
  2565.     // Get row -- this should never fail since Y should 
  2566.     //  have come from a valid cell!
  2567.     CEditTableRowElement* pRow = GetRow(Y);
  2568.     for( i = 0; i < number; i++ )
  2569.     {
  2570.         while( pRow )
  2571.         {
  2572.             // Must get next row now since we might delete it
  2573.             CEditTableRowElement *pNextRow = pRow->GetNextRow();
  2574.  
  2575.             // Check if all cells in the row will be deleted
  2576.             XP_Bool bDeleteRow = TRUE;
  2577.             CEditTableCellElement *pCell = pRow->GetFirstCell();
  2578.             while( pCell )
  2579.             {
  2580.                 int32 iCellY = pCell->GetY();
  2581.                 // We keep row if any cell is not at same top
  2582.                 //   or we will reduce rowspan rather than delete cell
  2583.                 if( (Y != iCellY) ||
  2584.                     (pCell->GetRowSpan() > number) )
  2585.                 {
  2586.                     bDeleteRow = FALSE;
  2587.                     break;
  2588.                 }    
  2589.                 pCell = pCell->GetNextCellInLogicalRow();
  2590.             }
  2591.  
  2592.             if( bDeleteRow )
  2593.             {
  2594.                 if ( pUndoContainer )
  2595.                 {
  2596.                     pRow->Unlink();
  2597.                     pRow->InsertAsLastChild(pUndoContainer);
  2598.                 }
  2599.                 else {
  2600.                     delete pRow;
  2601.                 }
  2602.             }
  2603.             // Get all cells that are at or span (TRUE param) given Y value
  2604.             pCell = GetFirstCellInRow(Y, TRUE);
  2605.             while( pCell )
  2606.             {
  2607.                 intn iRowSpan = pCell->GetRowSpan();
  2608.                 CEditTableCellElement *pNextCell = pCell->GetNextCellInRow();
  2609.                 if( pCell->GetY() == Y)
  2610.                 {
  2611.                     if( iRowSpan > number )
  2612.                     {
  2613.                         pCell->DecreaseRowSpan(number);
  2614.                     } else {
  2615.                         //TODO: CAN'T USE pUndoContainer - this wants a CEditTableRowElement*
  2616.                         pRow->DeleteCells(pCell->GetX(), pCell->GetColSpan() /*, pUndoContainer*/);
  2617.                     }
  2618.                     break;
  2619.                 } else {
  2620.                     // Cell is actually in row above
  2621.                     // The amount available is simpler than for columns,
  2622.                     //  since we always know what row we're in
  2623.                     int32 iSpanAvailable = iRowSpan - pCell->GetRow();
  2624.                     if( iSpanAvailable > 0 )
  2625.                     {
  2626.                         // Decresae the column span instead of deleting
  2627.                         intn iDecrease = min(iSpanAvailable, number);
  2628.                         pCell->DecreaseRowSpan(iDecrease);
  2629.                     } else {
  2630.                         // Should never get here?
  2631.                         XP_ASSERT(FALSE);
  2632.                     }
  2633.                 }
  2634.                 pCell = pNextCell;
  2635.             }
  2636.             // Next row. TODO: THIS IS NOT ENTIRELY CORRECT: ROWSPAN MAY CAUSE UNEVEN ROWS BELOW
  2637.             // (We need to track number of rows deleted (or RowSpan reduced) for each column)
  2638.             pRow = pNextRow;
  2639.         }
  2640.         
  2641.     } 
  2642. }
  2643.  
  2644. void CEditTableElement::DeleteColumns(int32 X, intn number, CEditTableElement* pUndoContainer){
  2645.     if ( number == 0 )
  2646.     {
  2647.         return; /* A waste of time, but legal. */
  2648.     }
  2649.     // Note: ClearTableAndCellSelection() is done in DeleteCells();
  2650.     if ( number < 0 || X < 0 )
  2651.      {
  2652.         /* Illegal. */
  2653.         XP_ASSERT(FALSE);
  2654.         return;
  2655.     }
  2656.     CEditTableRowElement* pRow = GetFirstRow();
  2657.     
  2658.     while( pRow )
  2659.     {
  2660.         CEditTableRowElement* pUndoRow = NULL;
  2661.         if ( pUndoContainer )
  2662.         {
  2663.             pUndoRow = new CEditTableRowElement();
  2664.             pUndoRow->InsertAsLastChild(pUndoContainer);
  2665.         }
  2666.         pRow->DeleteCells(X, number, pUndoRow);
  2667.         CEditTableRowElement *pNextRow = pRow->GetNextRow();
  2668.  
  2669.         // Check if ALL cells were deleted - delete row if all were
  2670.         if( !pRow->GetChild() )
  2671.             delete pRow;
  2672.  
  2673.         pRow = pNextRow;
  2674.     }
  2675. }
  2676.  
  2677. // This is the old version - just finds row based on counting,
  2678. //   no ROWSPAN effect needed
  2679. CEditTableRowElement* CEditTableElement::FindRow(intn number)
  2680. {
  2681.     intn count = 0;
  2682.     if ( number < 0 )
  2683.     {
  2684.         XP_ASSERT(FALSE);
  2685.         return NULL;
  2686.     }
  2687.     for ( CEditElement* pChild = GetChild();
  2688.             pChild;
  2689.                 pChild = pChild->GetNextSibling())
  2690.     {
  2691.         if ( pChild->IsTableRow() )
  2692.         {
  2693.             if ( count++ == number )
  2694.             {
  2695.                 return (CEditTableRowElement*) pChild;
  2696.             }
  2697.         }
  2698.     }
  2699.     return NULL;
  2700. }
  2701.  
  2702. CEditCaptionElement* CEditTableElement::GetCaption(){
  2703.     // Normally first or last, but we check everybody to be robust.
  2704.     for ( CEditElement* pChild = GetChild();
  2705.         pChild;
  2706.         pChild = pChild->GetNextSibling()) {
  2707.         if ( pChild->IsCaption() ) {
  2708.             return (CEditCaptionElement*) pChild;
  2709.         }
  2710.     }
  2711.     return NULL;
  2712. }
  2713.  
  2714. void CEditTableElement::SetCaption(CEditCaptionElement* pCaption){
  2715.     DeleteCaption();
  2716.     if ( pCaption ){
  2717.         EDT_TableCaptionData* pData = pCaption->GetData(GetWinCSID());
  2718.         XP_Bool bAddAtTop = ! ( pData->align == ED_ALIGN_BOTTOM || pData->align == ED_ALIGN_ABSBOTTOM);
  2719.         CEditCaptionElement::FreeData(pData);
  2720.         if ( bAddAtTop ){
  2721.             pCaption->InsertAsFirstChild(this);
  2722.         }
  2723.         else {
  2724.             pCaption->InsertAsLastChild(this);
  2725.         }
  2726.     }
  2727. }
  2728.  
  2729. void CEditTableElement::DeleteCaption(){
  2730.     CEditCaptionElement* pOldCaption = GetCaption();
  2731.     delete pOldCaption;
  2732. }
  2733.  
  2734. void CEditTableElement::FinishedLoad( CEditBuffer* pBuffer ){
  2735.     // From experimentation, we know that the 2.0 navigator dumps
  2736.     // tags that aren't in td, tr, or caption cells into the doc
  2737.     // before the table.
  2738.     //
  2739.     // That's a little too radical for me. So what I do is
  2740.     // wrap tds in trs, put anything else
  2741.     // into a caption
  2742.  
  2743.     CEditCaptionElement* pCaption = NULL;
  2744.     CEditTableRowElement* pRow = NULL;
  2745.     CEditElement* pNext = 0;
  2746.     
  2747.     if ( GetRows() <= 0 ) {
  2748.         // Force a row
  2749.         CEditTableRowElement* pTempRow = new CEditTableRowElement();
  2750.         pTempRow->InsertAsFirstChild(this);
  2751.     }
  2752.     
  2753.     CEditElement* pChild;
  2754.     for ( pChild = GetChild();
  2755.         pChild;
  2756.         pChild = pNext) {
  2757.         pNext = pChild->GetNextSibling(); // We might unlink pChild
  2758.         if ( ! IsAcceptableChild(*pChild) ){
  2759.             if ( pChild->IsTableCell() ){
  2760.                 if ( ! pRow ){
  2761.                     pRow = new CEditTableRowElement();
  2762.                     pRow->InsertAfter(pChild);
  2763.                 }
  2764.                 pChild->Unlink();
  2765.                 pChild->InsertAsLastChild(pRow);
  2766.                 if ( pCaption ){
  2767.                     pCaption->FinishedLoad(pBuffer);
  2768.                     pCaption = NULL;
  2769.                 }
  2770.             }
  2771.             else {
  2772.                 // Put random children into a caption
  2773.                 if ( ! pCaption ){
  2774.                     pCaption = new CEditCaptionElement();
  2775.                     pCaption->InsertAfter(pChild);
  2776.                 }
  2777.                 pChild->Unlink();
  2778.                 pChild->InsertAsLastChild(pCaption);
  2779.                 if ( pRow ){
  2780.                     pRow->FinishedLoad(pBuffer);
  2781.                     pRow = NULL;
  2782.                 }
  2783.             }
  2784.         }
  2785.         else {
  2786.             if ( pRow ){
  2787.                 pRow->FinishedLoad(pBuffer);
  2788.                 pRow = NULL;
  2789.             }
  2790.             if ( pCaption ){
  2791.                 pCaption->FinishedLoad(pBuffer);
  2792.                 pCaption = NULL;
  2793.             }
  2794.         }
  2795.         pChild->FinishedLoad(pBuffer);
  2796.     }
  2797.     if ( pRow ){
  2798.         pRow->FinishedLoad(pBuffer);
  2799.         pRow = NULL;
  2800.     }
  2801.     if ( pCaption ){
  2802.         pCaption->FinishedLoad(pBuffer);
  2803.         pCaption = NULL;
  2804.     }
  2805.  
  2806.     // Merge all captions together.
  2807.     pCaption = NULL;
  2808.     for ( pChild = GetChild();
  2809.         pChild;
  2810.         pChild = pNext) {
  2811.         pNext = pChild->GetNextSibling(); // We might unlink pChild
  2812.         if ( pChild->IsCaption() ){
  2813.             if ( pCaption ) {
  2814.                 // This is an extra caption. Merge its contents into the other caption.
  2815.                 pChild->Unlink();
  2816.                 CEditElement* pItem;
  2817.                 while ( ( pItem = pChild->GetChild() ) != NULL ){
  2818.                     pItem->Unlink();
  2819.                     pItem->InsertAsLastChild(pCaption);
  2820.                 }
  2821.                 delete pChild;
  2822.             }
  2823.             else {
  2824.                 pCaption = (CEditCaptionElement*) pChild;
  2825.             }
  2826.         }
  2827.     }
  2828.     AdjustCaption();
  2829.  
  2830.     EnsureSelectableSiblings(pBuffer);
  2831. }
  2832.  
  2833. void CEditTableElement::AdjustCaption() {
  2834.     CEditCaptionElement* pCaption = GetCaption();
  2835.     if ( pCaption ) {
  2836.         // Now move the caption to the start or the end of the table, as appropriate.
  2837.         pCaption->Unlink();
  2838.         SetCaption(pCaption);
  2839.     }
  2840. }
  2841.  
  2842. void CEditTableElement::StreamOut( IStreamOut *pOut){
  2843.     CEditElement::StreamOut( pOut );
  2844.     pOut->WriteInt( (int32)m_align );
  2845.     pOut->WriteInt( (int32)m_malign );
  2846. }
  2847.  
  2848. void CEditTableElement::SetData( EDT_TableData *pData ){
  2849.     char *pNew = CreateTagData(pData, FALSE);
  2850.     CEditElement::SetTagData( pNew );
  2851.     m_align = pData->align;
  2852.     m_malign = pData->malign; // Keep track of alignment seperately to avoid crashing when editing.
  2853.     if ( pNew ) {
  2854.         free(pNew); // XP_FREE?
  2855.     }
  2856.     // Save these since they are not saved in tag data if bWidthPercent = FALSE
  2857.     m_iWidthPixels = pData->iWidthPixels;
  2858.     m_iWidth = pData->iWidth;
  2859.     m_bWidthPercent = pData->bWidthPercent;
  2860.  
  2861.     m_iHeightPixels = pData->iHeightPixels;
  2862.     m_iHeight = pData->iHeight;
  2863.     m_bHeightPercent = pData->bHeightPercent;
  2864.  
  2865.     m_iInterCellSpace = pData->iInterCellSpace;
  2866.     m_iRows = pData->iRows;
  2867.     m_iMaxColumns = pData->iColumns;
  2868. }
  2869.  
  2870. char* CEditTableElement::CreateTagData(EDT_TableData *pData, XP_Bool bPrinting) {
  2871.     char *pNew = 0;
  2872.     m_align = pData->align;
  2873.     m_malign = pData->malign;
  2874.     if( bPrinting && pData->malign != ED_ALIGN_DEFAULT ){
  2875.         pNew = PR_sprintf_append( pNew, "ALIGN=%s ", EDT_AlignString(pData->malign) );
  2876.     }
  2877.     // If you don't mention the border, you get a zero pixel boarder.
  2878.     // If you just say BORDER, it's like BOARDER=1
  2879.     if ( pData->bBorderWidthDefined ){
  2880.         if ( pData->iBorderWidth == 1 ) {
  2881.             /* Neatness counts. */
  2882.             pNew = PR_sprintf_append( pNew, "BORDER ");
  2883.         }
  2884.         else {
  2885.             pNew = PR_sprintf_append( pNew, "BORDER=%d ", pData->iBorderWidth );
  2886.         }
  2887.     }
  2888.     if ( pData->iCellSpacing != 1 ){
  2889.         pNew = PR_sprintf_append( pNew, "CELLSPACING=%d ", pData->iCellSpacing );
  2890.     }
  2891.     if ( pData->iCellPadding != 1 ){
  2892.         pNew = PR_sprintf_append( pNew, "CELLPADDING=%d ", pData->iCellPadding );
  2893.     }
  2894.     if ( pData->bUseCols ){
  2895.         int cols = (int) GetColumns();
  2896.         if ( cols == 0 ) {
  2897.             cols = (int) pData->iColumns;
  2898.         }
  2899.         pNew = PR_sprintf_append( pNew, "COLS=%d ", cols );
  2900.     }
  2901.     if( pData->bWidthDefined ){
  2902.         if( pData->bWidthPercent ){
  2903.             pNew = PR_sprintf_append( pNew, "WIDTH=\"%ld%%\" ", 
  2904.                                       (long)min(100,max(1,pData->iWidth)) );
  2905.         }
  2906.         else {
  2907.             pNew = PR_sprintf_append( pNew, "WIDTH=\"%ld\" ", (long)pData->iWidth );
  2908.         }
  2909.     }
  2910.     if( pData->bHeightDefined ){
  2911.         if( pData->bHeightPercent ){
  2912.             pNew = PR_sprintf_append( pNew, "HEIGHT=\"%ld%%\" ", 
  2913.                                       (long)min(100,max(1, pData->iHeight)) );
  2914.         }
  2915.         else {
  2916.             pNew = PR_sprintf_append( pNew, "HEIGHT=\"%ld\" ", (long)pData->iHeight );
  2917.         }
  2918.     }
  2919.     if ( pData->pColorBackground ) {
  2920.         SetBackgroundColor(EDT_LO_COLOR(pData->pColorBackground));
  2921.         pNew = PR_sprintf_append( pNew, "BGCOLOR=\"#%06lX\" ", GetBackgroundColor().GetAsLong() );
  2922.     }
  2923.     else {
  2924.         SetBackgroundColor(ED_Color::GetUndefined());
  2925.     }
  2926.     if ( pData->pBackgroundImage ) {
  2927.         SetBackgroundImage(pData->pBackgroundImage);
  2928.         pNew = PR_sprintf_append( pNew, "BACKGROUND=\"%s\" ", pData->pBackgroundImage );
  2929.     }
  2930.     else {
  2931.         SetBackgroundImage(0);
  2932.     }
  2933.     if ( pData->bBackgroundNoSave) {
  2934.         pNew = PR_sprintf_append( pNew, "NOSAVE " );
  2935.     }
  2936.  
  2937.     if( pData->pExtra  ){
  2938.         pNew = PR_sprintf_append( pNew, "%s ", pData->pExtra );
  2939.     }
  2940.  
  2941.     if( pNew ){
  2942.         pNew = PR_sprintf_append( pNew, ">" );
  2943.     }
  2944.     return pNew;
  2945. }
  2946.  
  2947. EDT_TableData* CEditTableElement::GetData()
  2948. {
  2949.     EDT_TableData *pRet;
  2950.     PA_Tag* pTag = CEditElement::TagOpen(0);
  2951.  
  2952.     pRet = ParseParams( pTag, GetWinCSID() );
  2953.     // Alignment isn't kept in the tag data, because tag data is used for editing,
  2954.     // and we crash if we try to edit an aligned table, because layout doesn't
  2955.     // set up the data structures that we need.
  2956.     pRet->align = m_align;
  2957.     pRet->malign = m_malign;
  2958.     // Return the widths determined by layout
  2959.     pRet->iWidth = m_iWidth;
  2960.     pRet->iWidthPixels = m_iWidthPixels;
  2961.  
  2962.     pRet->iHeight = m_iHeight;
  2963.     pRet->iHeightPixels = m_iHeightPixels;
  2964.  
  2965.     pRet->iInterCellSpace = m_iInterCellSpace;
  2966.     pRet->iColumns = m_iMaxColumns;
  2967.     PA_FreeTag( pTag );
  2968.     return pRet;
  2969. }
  2970.  
  2971. static char *tableParams[] = {
  2972.     PARAM_ALIGN,
  2973.     PARAM_BORDER,
  2974.     PARAM_CELLSPACE,
  2975.     PARAM_CELLPAD,
  2976.     PARAM_COLS,
  2977.     PARAM_WIDTH,
  2978.     PARAM_HEIGHT,
  2979.     PARAM_BGCOLOR,
  2980.     PARAM_BACKGROUND,
  2981.     PARAM_NOSAVE,
  2982.     0
  2983. };
  2984.  
  2985. EDT_TableData* CEditTableElement::ParseParams( PA_Tag *pTag, int16 csid ){
  2986.     EDT_TableData *pData = NewData();
  2987.     ED_Alignment malign;
  2988.     
  2989.     malign = edt_FetchParamAlignment( pTag, ED_ALIGN_DEFAULT, FALSE, csid );
  2990.     // Center not supported in 3.0
  2991.     if( malign == ED_ALIGN_RIGHT || malign == ED_ALIGN_LEFT || malign == ED_ALIGN_DEFAULT ){
  2992.         pData->malign = malign;
  2993.     }
  2994.  
  2995.     pData->iRows = GetRows();
  2996.     pData->iColumns = GetColumns();
  2997.     // If you just say BORDER, it's the same as BORDER=1
  2998.     // If you say BORDER=0, it's different than not saying BORDER at all
  2999.     pData->bBorderWidthDefined = edt_FetchParamBoolExist(pTag, PARAM_BORDER, csid);
  3000.     pData->iBorderWidth = edt_FetchParamInt(pTag, PARAM_BORDER, 0, 1, csid);
  3001.     pData->iCellSpacing = edt_FetchParamInt(pTag, PARAM_CELLSPACE, 1, csid);
  3002.     pData->iCellPadding = edt_FetchParamInt(pTag, PARAM_CELLPAD, 1, csid);
  3003.     {
  3004.         // If we're parsing a table tag, and we've hit a cols= property, we
  3005.         // need to remember the number of columns.
  3006.         int columns = edt_FetchParamInt(pTag, PARAM_COLS, 0, csid);
  3007.         pData->bUseCols = columns > 0;
  3008.         if ( pData->bUseCols ) {
  3009.             pData->iColumns = columns;
  3010.         }
  3011.     }
  3012.     pData->bWidthDefined = edt_FetchDimension( pTag, PARAM_WIDTH, 
  3013.                     &pData->iWidth, &pData->bWidthPercent,
  3014.                     100, TRUE, csid );
  3015.     pData->bHeightDefined = edt_FetchDimension( pTag, PARAM_HEIGHT, 
  3016.                     &pData->iHeight, &pData->bHeightPercent,
  3017.                     100, TRUE, csid );
  3018.  
  3019.     // If width and/or height are NOT defined, use the
  3020.     //    "bPercent" values set by any previous SetData() calls
  3021.     if( !pData->bWidthDefined )
  3022.         pData->bWidthPercent = m_bWidthPercent;
  3023.     if( !pData->bHeightDefined )
  3024.         pData->bHeightPercent = m_bHeightPercent;
  3025.  
  3026.  
  3027.     pData->pColorBackground = edt_MakeLoColor(edt_FetchParamColor( pTag, PARAM_BGCOLOR, csid ));
  3028.     pData->pBackgroundImage = edt_FetchParamString( pTag, PARAM_BACKGROUND, csid );
  3029.     pData->bBackgroundNoSave = edt_FetchParamBoolExist( pTag, PARAM_NOSAVE, csid );
  3030.     pData->pExtra = edt_FetchParamExtras( pTag, tableParams, csid );
  3031.  
  3032.     return pData;
  3033. }
  3034.  
  3035. EDT_TableData* CEditTableElement::NewData(){
  3036.     EDT_TableData *pData = XP_NEW( EDT_TableData );
  3037.     if( pData == 0 ){
  3038.         // throw();
  3039.         return pData;
  3040.     }
  3041.     pData->align = ED_ALIGN_LEFT;
  3042.     pData->malign = ED_ALIGN_DEFAULT;
  3043.     pData->bUseCols = TRUE;
  3044.     pData->iRows = 1;
  3045.     pData->iColumns = 2;
  3046.     pData->bBorderWidthDefined = TRUE;
  3047.     pData->iBorderWidth = 2; //1; //cmanske: Increase these for easier selection?
  3048.     pData->iCellSpacing = 2; //1;
  3049.     pData->iCellPadding = 3; //1; Extra here makes it easier to place caret
  3050.     pData->bWidthDefined = TRUE;
  3051.     pData->bWidthPercent = TRUE;
  3052.     pData->iWidth = 100;
  3053.     pData->iWidthPixels = 0;
  3054.     pData->iInterCellSpace = 0;
  3055.     pData->bHeightDefined = FALSE;
  3056.     pData->bHeightPercent = TRUE;
  3057.     pData->iHeight = 100;
  3058.     pData->pColorBackground = 0;
  3059.     pData->pBackgroundImage = 0;
  3060.     pData->bBackgroundNoSave = FALSE;
  3061.     pData->pExtra = 0;
  3062.     return pData;
  3063. }
  3064.  
  3065. void CEditTableElement::FreeData( EDT_TableData *pData ){
  3066.     if( pData->pColorBackground ) XP_FREE( pData->pColorBackground );
  3067.     if( pData->pBackgroundImage ) XP_FREE( pData->pBackgroundImage );
  3068.     if( pData->pExtra ) XP_FREE( pData->pExtra );
  3069.     XP_FREE( pData );
  3070. }
  3071.  
  3072. void CEditTableElement::SetBackgroundColor( ED_Color iColor ){
  3073.     m_backgroundColor = iColor;
  3074. }
  3075.  
  3076. ED_Color CEditTableElement::GetBackgroundColor(){
  3077.     return m_backgroundColor;
  3078. }
  3079.  
  3080. void CEditTableElement::SetBackgroundImage( char* pImage ){
  3081.     delete m_pBackgroundImage;
  3082.     m_pBackgroundImage = 0;
  3083.     if ( pImage ) {
  3084.         m_pBackgroundImage = XP_STRDUP(pImage);
  3085.     }
  3086. }
  3087.  
  3088. char* CEditTableElement::GetBackgroundImage(){
  3089.     return m_pBackgroundImage;
  3090. }
  3091.  
  3092. void CEditTableElement::PrintOpen( CPrintState *pPrintState ){
  3093.     pPrintState->m_iCharPos = 0;
  3094.     pPrintState->m_pOut->Printf( "\n"); 
  3095.  
  3096.     PA_Tag *pTag = InternalTagOpen(0, TRUE);
  3097.     while( pTag ){
  3098.         char *pData = 0;
  3099.         if( pTag->data ){
  3100.             PA_LOCK( pData, char*, pTag->data );
  3101.         }
  3102.  
  3103.         if( pData && *pData != '>' ) {
  3104.             pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "<%s %s", 
  3105.                     EDT_TagString(pTag->type), pData );
  3106.         }
  3107.         else {
  3108.             pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "<%s>", 
  3109.                     EDT_TagString(pTag->type));
  3110.         }
  3111.  
  3112.         if( pTag->data ){
  3113.             PA_UNLOCK( pTag->data );
  3114.         }
  3115.  
  3116.         PA_Tag* pDelTag = pTag;
  3117.         pTag = pTag->next;
  3118.         PA_FreeTag(pDelTag);
  3119.     }
  3120. }
  3121.  
  3122. void CEditTableElement::PrintEnd( CPrintState *pPrintState ){
  3123.     PA_Tag *pTag = TagEnd();
  3124.     while( pTag ){
  3125.         char *pData = 0;
  3126.         if( pTag->data ){
  3127.             PA_LOCK( pData, char*, pTag->data );
  3128.         }
  3129.  
  3130.         pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "</%s>",
  3131.             EDT_TagString( pTag->type ) );
  3132.  
  3133.         if( pTag->data ){
  3134.             PA_UNLOCK( pTag->data );
  3135.         }
  3136.  
  3137.         PA_Tag* pDelTag = pTag;
  3138.         pTag = pTag->next;
  3139.         PA_FreeTag(pDelTag);
  3140.     }
  3141.  
  3142.     pPrintState->m_iCharPos = 0;
  3143.     pPrintState->m_pOut->Printf( "\n"); 
  3144. }
  3145.  
  3146. LO_TableStruct* CEditTableElement::GetLoTable()
  3147. {
  3148.     // Find the first leaf element in the table since
  3149.     //  it will have pointer to LO_Element
  3150.     CEditElement *pLeaf = FindNextElement(&CEditElement::FindLeafAll,0 );
  3151.     if( pLeaf )
  3152.     {
  3153.         // Only leaf edit elements have their pointers saved in LO_Elements
  3154.         LO_Element *pLoElement = pLeaf->Leaf()->GetLayoutElement();
  3155.         if( pLoElement )
  3156.         {
  3157.             // Find enclosing LO cell
  3158.             LO_TableStruct *pLoTable = lo_GetParentTable(GetEditBuffer()->m_pContext, pLoElement);
  3159.             // We shoul ALWAYS find a table
  3160.             XP_ASSERT(pLoTable);
  3161.             return pLoTable;
  3162.         }
  3163.     }
  3164.     return NULL;
  3165. }
  3166.  
  3167. void CEditTableElement::GetParentSize(MWContext *pContext, int32 *pWidth, int32 *pHeight, LO_TableStruct *pLoTable)
  3168. {
  3169.     // This may be supplied -- get it if not
  3170.     if( !pLoTable )
  3171.         pLoTable = GetLoTable();
  3172.  
  3173.     int32 iMaxWidth = 1;
  3174.     int32 iMaxHeight = 1;
  3175.  
  3176.     if( pLoTable )
  3177.     {
  3178.         // Get viewing window
  3179.         int32 iXOrigin, iYOrigin;
  3180.         FE_GetDocAndWindowPosition(pContext, &iXOrigin, &iYOrigin, &iMaxWidth, &iMaxHeight);
  3181.  
  3182.         // Get extra margin
  3183.         int32 iMarginWidth;
  3184.         int32 iMarginHeight;
  3185.         LO_GetDocumentMargins(pContext, &iMarginWidth, &iMarginHeight);
  3186.  
  3187.         // Check for embeded table - get max width from Containing cell
  3188.         LO_CellStruct *pParentCell = lo_GetParentCell( pContext, (LO_Element*)pLoTable );
  3189.  
  3190.         if( pParentCell  )
  3191.         {
  3192.             iMaxWidth = pParentCell->width - (2 * pParentCell->border_horiz_space);
  3193.             iMaxHeight = pParentCell->height - (2 * pParentCell->border_vert_space);
  3194.         } else {
  3195.             iMaxWidth -= 2 * iMarginWidth;
  3196.             iMaxHeight -= 2 * iMarginHeight;
  3197.         }
  3198.     }
  3199.     if( pWidth )
  3200.         *pWidth = iMaxWidth;
  3201.     
  3202.     if( pHeight )
  3203.         *pHeight = iMaxHeight;
  3204. }
  3205.  
  3206. void CEditTableElement::SetTableMode(MWContext *pContext, int iMode)
  3207. {
  3208.  
  3209.     int32 iMaxWidth, iMaxHeight;
  3210.     XP_Bool bPercent = (XP_Bool)(iMode & ED_TABLE_PERCENT);
  3211.     XP_Bool bPixels = (XP_Bool)(iMode & ED_TABLE_PIXELS);
  3212.     XP_Bool bUseCols = (XP_Bool)(iMode & ED_TABLE_USE_COLS);
  3213.     XP_Bool bNoCols = (XP_Bool)(iMode & ED_TABLE_NO_COLS);
  3214.     XP_Bool bChanged = FALSE;
  3215.  
  3216.     if( (bPixels && m_bWidthPercent) || (bPercent && !m_bWidthPercent) || 
  3217.         (bPixels && m_bHeightPercent) || (bPercent && !m_bHeightPercent) ||
  3218.         bUseCols || bNoCols )
  3219.     {
  3220.         EDT_TableData *pTableData = GetData();
  3221.         if( !pTableData )
  3222.             return;
  3223.  
  3224.         GetParentSize(pContext, &iMaxWidth, &iMaxHeight);
  3225.  
  3226.         pTableData->bWidthPercent = bPercent;
  3227.         pTableData->bHeightPercent = bPercent;
  3228.         if( bPercent )
  3229.         {
  3230.             pTableData->iWidth = m_iWidthPixels * 100 / iMaxWidth;
  3231.             pTableData->bWidthDefined = TRUE;
  3232.             pTableData->iHeight = m_iHeightPixels * 100 / iMaxHeight;
  3233.             pTableData->bHeightDefined = TRUE;
  3234.         } else if( bPixels ){
  3235.             pTableData->iWidth = m_iWidthPixels;    
  3236.             pTableData->bWidthDefined = TRUE;
  3237.             pTableData->iHeight = m_iHeightPixels;
  3238.             pTableData->bHeightDefined = TRUE;
  3239.         }
  3240.         if( bUseCols )
  3241.             pTableData->bUseCols = TRUE;
  3242.         else if( bNoCols )
  3243.             pTableData->bUseCols = FALSE;
  3244.  
  3245.         SetData(pTableData);
  3246.         EDT_FreeTableData(pTableData);
  3247.     }
  3248.  
  3249.  
  3250.     // Scan through entire table to reset all rows and cells
  3251.     CEditElement  *pRow = GetChild();
  3252.     CEditTableCellElement *pCell;
  3253.     int iRow = 0;
  3254.     while( pRow )
  3255.     {
  3256.         if( pRow->IsTableRow() )
  3257.         {
  3258.             pCell = (CEditTableCellElement*)(pRow->GetChild());
  3259.  
  3260.             if( pCell )
  3261.             {
  3262.                 iMaxWidth = pCell->GetParentWidth();
  3263.                 iMaxHeight = pCell->GetParentHeight();
  3264.             }
  3265.             else 
  3266.             {
  3267.                 XP_TRACE(("ROW HAS NO CHILD!"));
  3268.             }
  3269.             while( pCell )
  3270.             {
  3271.                 bPercent = iMode & ED_TABLE_PERCENT;
  3272.                 bPixels = iMode & ED_TABLE_PIXELS;
  3273.                 XP_Bool bSetSize = iMode & ED_TABLE_USE_CELL_WIDTH;
  3274.                 XP_Bool bClearSize = iMode & ED_TABLE_NO_CELL_WIDTH;
  3275.                 bChanged = FALSE;
  3276.                 if( bPixels || bPercent || bSetSize || bClearSize )
  3277.                 {
  3278.                     EDT_TableCellData *pCellData = pCell->GetData();
  3279.                     if( pCellData )
  3280.                     {
  3281.                         if( (bPercent && !pCellData->bWidthPercent) ||
  3282.                             (bPixels && pCellData->bWidthPercent) )
  3283.                         {
  3284.                             pCellData->bWidthPercent = bPercent;
  3285.                             if( bPercent )
  3286.                                 pCellData->iWidth = (pCellData->iWidthPixels * 100) / iMaxWidth;
  3287.                             else
  3288.                                 pCellData->iWidth = pCellData->iWidthPixels;
  3289.                             bChanged = TRUE;
  3290.                         }
  3291.                         if( (pCellData->bWidthDefined && bClearSize) ||
  3292.                             (!pCellData->bWidthDefined && bSetSize) )
  3293.                         {
  3294.                             pCellData->bWidthDefined = bSetSize;
  3295.                             bChanged = TRUE;
  3296.                         }
  3297.                         if( bChanged )
  3298.                             pCell->SetData(pCellData);
  3299.                     
  3300.                         EDT_FreeTableCellData(pCellData);
  3301.                     }
  3302.                 }
  3303.                 pCell = (CEditTableCellElement*)pCell->GetNextSibling();
  3304.             }
  3305.         }
  3306.         pRow = pRow->GetNextSibling();
  3307.         iRow++;
  3308.     }
  3309. }
  3310.  
  3311. // class CEditTableRowElement
  3312.  
  3313. CEditTableRowElement::CEditTableRowElement()
  3314.     : CEditElement((CEditElement*) NULL, P_TABLE_ROW),
  3315.       m_backgroundColor(),
  3316.       m_iBackgroundSaveIndex(0),
  3317.       m_pBackgroundImage(0)
  3318. {
  3319. }
  3320.  
  3321. CEditTableRowElement::CEditTableRowElement(intn columns)
  3322.     : CEditElement((CEditElement*) NULL, P_TABLE_ROW),
  3323.       m_backgroundColor(),
  3324.       m_iBackgroundSaveIndex(0),
  3325.       m_pBackgroundImage(0)
  3326. {
  3327.     EDT_TRY {
  3328.         for ( intn column = 0; column < columns; column++ ) {
  3329.             CEditTableCellElement* pCell = new CEditTableCellElement();
  3330.             pCell->InsertAsFirstChild(this);
  3331.         }
  3332.     }
  3333.     EDT_CATCH_ALL {
  3334.         Finalize();
  3335.         EDT_RETHROW;
  3336.     }
  3337. }
  3338.  
  3339. CEditTableRowElement::CEditTableRowElement(CEditElement *pParent, PA_Tag *pTag, int16 /*csid*/)
  3340.     : CEditElement(pParent, P_TABLE_ROW),
  3341.       m_backgroundColor(),
  3342.       m_iBackgroundSaveIndex(0),
  3343.       m_pBackgroundImage(0)
  3344. {
  3345.     if( pTag ){
  3346.         char *locked_buff;
  3347.             
  3348.         PA_LOCK(locked_buff, char *, pTag->data );
  3349.         if( locked_buff ){
  3350.             SetTagData( locked_buff );
  3351.         }
  3352.         PA_UNLOCK(pTag->data);
  3353.     }
  3354. }
  3355.  
  3356. CEditTableRowElement::CEditTableRowElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer)
  3357.     : CEditElement(pStreamIn, pBuffer),
  3358.       m_backgroundColor(),
  3359.       m_iBackgroundSaveIndex(0),
  3360.       m_pBackgroundImage(0)
  3361. {
  3362. }
  3363.  
  3364. CEditTableRowElement::~CEditTableRowElement(){
  3365.     delete m_pBackgroundImage;
  3366. }
  3367.  
  3368. CEditTableRowElement *CEditTableRowElement::GetNextRow()
  3369. {
  3370.     CEditElement *pRow = GetNextSibling();
  3371.     if( pRow && pRow->IsTableRow() )
  3372.     {
  3373.         return pRow->TableRow();
  3374.     }
  3375.     return NULL;
  3376. }
  3377.  
  3378. CEditTableCellElement *CEditTableRowElement::GetFirstCell()
  3379. {
  3380.     CEditElement *pChild = GetChild();
  3381.     if( pChild && pChild->IsTableCell() )
  3382.     {
  3383.         return pChild->TableCell();
  3384.     }
  3385.     return NULL;
  3386. }
  3387.  
  3388. // Scan all cells in row and add up m_iColSpan to get 
  3389. //   maximum possible cells in the row
  3390. intn CEditTableRowElement::GetCells()
  3391. {
  3392.     intn cells = 0;
  3393.     // Get the first cell of the row
  3394.     CEditElement* pChild = GetChild();
  3395.  
  3396.     if( pChild && pChild->IsTableCell() )
  3397.     {
  3398.         // Starting number of cells is normally 0,
  3399.         //  except when ROWSPAN causes first cell to be indented
  3400.         while( pChild )
  3401.         {
  3402.             if ( pChild->IsTableCell() )
  3403.             {
  3404.                 // We must add column span to get maximum possible columns per row
  3405.                 cells += ((CEditTableCellElement*)pChild)->GetColSpan();
  3406.             }
  3407.             pChild = pChild->GetNextSibling();
  3408.         }
  3409.     }
  3410.     return cells;
  3411. }
  3412.  
  3413. void CEditTableRowElement::FinishedLoad( CEditBuffer* pBuffer ){
  3414.     // Wrap anything that's not a table cell in a table cell.
  3415.     CEditTableCellElement* pCell = NULL;
  3416.     CEditElement* pNext = 0;
  3417.     
  3418.     if ( ! GetChild() ) {
  3419.         // Force a cell
  3420.         CEditTableCellElement* pTempCell = new CEditTableCellElement();
  3421.         pTempCell->InsertAsFirstChild(this);
  3422.     }
  3423.  
  3424.     for ( CEditElement* pChild = GetChild();
  3425.         pChild;
  3426.         pChild = pNext) {
  3427.         pNext = pChild->GetNextSibling(); // We might unlink pChild
  3428.         if ( ! IsAcceptableChild(*pChild) ){
  3429.             if ( ! pCell ){
  3430.                 pCell = new CEditTableCellElement();
  3431.                 pCell->InsertAfter(pChild);
  3432.             }
  3433.             pChild->Unlink();
  3434.             pChild->InsertAsLastChild(pCell);
  3435.         }
  3436.         else {
  3437.             if ( pCell ){
  3438.                 pCell->FinishedLoad(pBuffer);
  3439.                 pCell = NULL;
  3440.             }
  3441.         }
  3442.         pChild->FinishedLoad(pBuffer);
  3443.     }
  3444.     if ( pCell ){
  3445.         pCell->FinishedLoad(pBuffer);
  3446.         pCell = NULL;
  3447.     }
  3448. }
  3449.  
  3450. static void edt_InsertCells(intn number, XP_Bool bAfter, CEditTableCellElement *pExisting, CEditTableRowElement* pSource)
  3451. {
  3452.     CEditTableCellElement *pInsertCell = NULL;
  3453.     XP_ASSERT(pExisting);
  3454.     if( !pExisting )
  3455.         return;
  3456.     CEditBuffer *pBuffer = pExisting->GetEditBuffer();
  3457.     if( !pBuffer )
  3458.         return;
  3459.  
  3460.     // Insert new cells at new insert position
  3461.     for ( intn col = 0; col < number; col++ )
  3462.     {
  3463.         if ( pSource )
  3464.         {
  3465.             // Remove cells from source from left to right
  3466.             pInsertCell = pSource->GetFirstCell();
  3467.             pInsertCell->Unlink();
  3468.  
  3469.             // If inserted cell spans > 1 column, then
  3470.             //   we should insert fewer to keep table rectangular
  3471.             intn iColSpan = pInsertCell->GetColSpan();
  3472.             if( iColSpan > 1 )
  3473.                 number -= iColSpan - 1;
  3474.         }
  3475.         else {
  3476.             pInsertCell = new CEditTableCellElement();
  3477.         }
  3478.         if( pInsertCell )
  3479.         {
  3480.             if( bAfter )
  3481.             {
  3482.                 pInsertCell->InsertAfter(pExisting);
  3483.                 // To insert new cells when pSource is used
  3484.                 //  and we want to insert from left to right,
  3485.                 //  set the "before" cell to the one just inserted
  3486.                 pExisting = pInsertCell;
  3487.             }
  3488.             else
  3489.             {
  3490.                 // Insert before the current column
  3491.                 // This will add pSource cells from left to right
  3492.                 pInsertCell->InsertBefore(pExisting);
  3493.             }
  3494.             // Be sure cell has the empty text element in default paragraph container
  3495.             pInsertCell->FinishedLoad(pBuffer);
  3496.         }
  3497.     }
  3498. }
  3499.  
  3500. // Note: iNewX should either be == X (insert before current column)
  3501. //       or == X + (insert column's width).
  3502. void CEditTableRowElement::InsertCells(int32 X, int32 iNewX, intn number, CEditTableRowElement* pSource)
  3503. {
  3504.     if ( number <= 0 || X < 0 )
  3505.     {
  3506.         return;
  3507.     }
  3508.     CEditTableElement *pTable = (CEditTableElement*)GetParent();
  3509.     if( !pTable || !pTable->IsTable() )
  3510.         return;
  3511.  
  3512.     int32 iInterCellSpace = pTable->GetInterCellSpace();
  3513.     int32 iCellX;
  3514.  
  3515.     if ( pTable->GetFirstColumnX() == iNewX )
  3516.     {
  3517.         // Insert at the start.
  3518.         for ( intn col = 0; col < number; col++ )
  3519.         {
  3520.             CEditTableCellElement* pInsertCell;
  3521.             if ( pSource )
  3522.             {
  3523.                 // Insert cells from the end
  3524.                 pInsertCell = (CEditTableCellElement*)pSource->GetLastChild();
  3525.                 pInsertCell->Unlink();
  3526.  
  3527.                 // If inserted cell spans > 1 column, then
  3528.                 //   we should insert fewer to keep table rectangular
  3529.                 intn iColSpan = pInsertCell->GetColSpan();
  3530.                 if( iColSpan > 1 )
  3531.                     number -= iColSpan - 1;
  3532.             }
  3533.             //Note: Create a new cell if source was missing enough cells
  3534.             if( !pInsertCell ) 
  3535.             {
  3536.                 pInsertCell = new CEditTableCellElement();
  3537.             }
  3538.             if( pInsertCell )
  3539.             {
  3540.                 pInsertCell->InsertAsFirstChild(this);
  3541.             }
  3542.         }
  3543.     }
  3544.     else 
  3545.     {
  3546.         // Get the cell at the requested column
  3547.         CEditTableCellElement* pExisting = FindCell(X, X != iNewX);
  3548.         // This may now be NULL for "missing" cells because
  3549.         //   of ROWSPAN or COLSPAN effects
  3550.         if( pExisting )
  3551.         {
  3552.             iCellX = pExisting->GetX();
  3553.             if( (iCellX < X || X != iNewX) && ((iCellX + pExisting->GetWidth()) > iNewX) )
  3554.             {
  3555.                 // Cell extends past new column insert point,
  3556.                 //  so simply increase span and don't add new cell
  3557.                 pExisting->IncreaseColSpan(number);
  3558.             } else {
  3559.                 // Non-spanned cell found at current column,
  3560.                 //   so insert before (X==iNewX) or after that cell
  3561.                 edt_InsertCells(number, X < iNewX, pExisting, pSource);
  3562.             }
  3563.         } 
  3564.         else if( (X != iNewX) && (pExisting = FindCell(iNewX+iInterCellSpace)) != NULL )
  3565.         {
  3566.             // No cell in current column, but we found cell at NEW insert column
  3567.             //  so put new cell(s) before this
  3568.             edt_InsertCells(number, FALSE, pExisting, pSource);
  3569.         }
  3570.         else
  3571.         {
  3572.             // We didn't find a cell, either because of cells spanning row
  3573.             //   from above, or this row needs new cell(s) at the end
  3574.  
  3575.             // First cell in current row
  3576.             CEditTableCellElement *pFirstCell = GetFirstCell();
  3577.             if( !pFirstCell )
  3578.                 return;
  3579.             // This should be same as pFirstCell, but we need to setup
  3580.             //  to find other cells in same geometric row
  3581.             CEditTableCellElement *pCell = pTable->GetFirstCellInRow(pFirstCell->GetY());
  3582.             XP_ASSERT(pCell == pFirstCell);
  3583.             intn iCurrentRow = pFirstCell->GetRow();
  3584.  
  3585.             CEditTableCellElement *pCellBefore = NULL;
  3586.             CEditTableCellElement *pCellAfter = NULL;
  3587.  
  3588.             while(pCell)
  3589.             {
  3590.                 iCellX = pCell->GetX();
  3591.                 int32 iCellWidth = pCell->GetWidth();
  3592.                 if( iCellX == X )
  3593.                 {
  3594.                     // We found a cell from another row that spans current row
  3595.                     //  and target column, so just increase its column span instead of inserting
  3596.                     if( X != iNewX && (iCellX+iCellWidth) > iNewX )
  3597.                     {
  3598.                         pCell->IncreaseColSpan(number);
  3599.                         return;
  3600.                     }
  3601.                 }
  3602.                 // Find the closest cells in current row
  3603.                 //   to our target column             
  3604.                 if( pCell->GetRow() == iCurrentRow )
  3605.                 {
  3606.                     if( (iCellX + pCell->GetWidth()) <= X )
  3607.                         pCellBefore = pCell;
  3608.                     if( iCellX >= X && !pCellAfter )
  3609.                         pCellAfter = pCell;
  3610.                 }
  3611.                 pCell = pTable->GetNextCellInRow();
  3612.             }
  3613.             if( pCellAfter )
  3614.             {
  3615.                 // Force inserting before the cell found
  3616.                 edt_InsertCells(number, FALSE, pCellAfter, pSource);
  3617.                 return;
  3618.             }
  3619.             if( pCellBefore )
  3620.             {
  3621.                 // This is tough. We know we need to add cells, but how many?
  3622.                 // We can't be sure we had enough before so new cells end up
  3623.                 //   at desired new column. TODO: For now, just add and come back to this later
  3624.                 edt_InsertCells(number, TRUE, pCellBefore, pSource);
  3625.             }
  3626.         }
  3627.     }
  3628. }
  3629.  
  3630. intn CEditTableRowElement::AppendRow( CEditTableRowElement *pAppendRow, XP_Bool bDeleteRow )
  3631. {
  3632.     if( !pAppendRow )
  3633.         return 0;
  3634.  
  3635.     intn iColumnsAppended = 0;
  3636.     CEditTableCellElement *pAppendCell = pAppendRow->GetFirstCell();
  3637.  
  3638.     while( pAppendCell )
  3639.     {
  3640.         // Unlink each cell in source row and
  3641.         //   append it to current row
  3642.         pAppendCell->Unlink();
  3643.         pAppendCell->InsertAsLastChild(this);
  3644.         iColumnsAppended += pAppendCell->GetColSpan();
  3645.         pAppendCell = pAppendRow->GetFirstCell();
  3646.     }
  3647.  
  3648.     if( bDeleteRow )
  3649.         delete pAppendRow;
  3650.  
  3651.     return iColumnsAppended;
  3652. }
  3653.  
  3654. void CEditTableRowElement::PadRowWithEmptyCells( intn iColumns )
  3655. {
  3656.     CEditTableElement *pTable = (CEditTableElement*)GetParent();
  3657.     if( !pTable || !pTable->IsTable() )
  3658.         return;
  3659.     CEditTableCellElement *pCell = GetFirstCell();
  3660.     CEditTableCellElement *pLastCell = NULL;
  3661.  
  3662.     // Use number of columns supplied, or get current number in table
  3663.     intn iMaxColumns = iColumns ? iColumns : pTable->GetMaxColumns();
  3664.     intn iCurrentColumns = 0;
  3665.  
  3666.     while( pCell )
  3667.     {
  3668.         iCurrentColumns += pCell->GetColSpan();
  3669.         pLastCell = pCell;
  3670.         pCell = pCell->GetNextCellInLogicalRow();
  3671.     }
  3672.     if( pLastCell )
  3673.     {
  3674.         intn iExtraColumns = iMaxColumns - iCurrentColumns;
  3675.         if( iExtraColumns > 0 )
  3676.             edt_InsertCells( iExtraColumns, TRUE, pLastCell, NULL );
  3677.     }
  3678. }
  3679.  
  3680. static void edt_DeleteCell(CEditTableCellElement* pCell, CEditTableRowElement* pUndoContainer)
  3681. {
  3682.     if ( pUndoContainer )
  3683.     {
  3684.         pCell->Unlink();
  3685.         pCell->InsertAsFirstChild(pUndoContainer);
  3686.     }
  3687.     else {
  3688.         delete pCell;
  3689.     }
  3690. }
  3691.  
  3692. void CEditTableRowElement::DeleteCells(int32 X, intn number, CEditTableRowElement* pUndoContainer){
  3693.     if ( number == 0 )
  3694.     {
  3695.         return; /* A waste of time, but legal. */
  3696.     }
  3697.     GetEditBuffer()->ClearTableAndCellSelection();
  3698.     intn cells = GetCells();
  3699.     if ( number < 0 || X < 0 )
  3700.     {
  3701.         /* Illegal. */
  3702.         XP_ASSERT(FALSE);
  3703.         return;
  3704.     }
  3705.     CEditTableElement *pTable = (CEditTableElement*)GetParent();
  3706.     XP_ASSERT(pTable && pTable->IsTable() );
  3707.     CEditTableCellElement* pCell = FindCell(X);
  3708.     if( pTable && pCell )
  3709.     {
  3710.         intn iRemaining = number;
  3711.         while( iRemaining > 0 )
  3712.         {
  3713.             // Get next cell before we delete the one we're at
  3714.             CEditTableCellElement *pNextCell = pCell->GetNextCellInLogicalRow();
  3715.  
  3716.             int32 iCellX = pCell->GetX();
  3717.             int32 iColSpan = pCell->GetColSpan();
  3718.  
  3719.             if( X == iCellX )
  3720.             {
  3721.                 // Cell is in same column as delete column
  3722.                 if( iColSpan <= iRemaining )
  3723.                 {
  3724.                     // Simplest case - just delete one cell
  3725.                     edt_DeleteCell(pCell, pUndoContainer);
  3726.                     iRemaining -= iColSpan;
  3727.                 } 
  3728.                 else if( iColSpan > iRemaining )
  3729.                 {
  3730.                     // Cell spans more than we want to delete,
  3731.                     //  so reduce span instead of actually deleting
  3732.                     pCell->DecreaseColSpan(iRemaining);
  3733.                     iRemaining = 0;
  3734.                 }
  3735.             } else {
  3736.                 // Start of cell is before delete column
  3737.                 //  (but end must be past delete column)
  3738.                 // This case is difficult, since its hard 
  3739.                 //  to figure out how many columns it spans before
  3740.                 //  delete column. This may be underestimated,
  3741.                 //  resulting in too much reduction in column span,
  3742.                 //    but since contents aren't deleted, its not too risky
  3743.                 int32 iSpanAvailable = iColSpan - pTable->GetColumnsSpanned(iCellX, X);
  3744.                 if( iSpanAvailable > 0 )
  3745.                 {
  3746.                     // Decrease the column span instead of deleting
  3747.                     intn iDecrease = min(iSpanAvailable,iRemaining);
  3748.                     pCell->DecreaseColSpan(iDecrease);
  3749.                     iRemaining -= iDecrease;
  3750.                 }
  3751.             }
  3752.             // We are done if no more cells in row or nothing remaining to delete
  3753.             if( !pNextCell )
  3754.                 break;
  3755.  
  3756.             pCell = pNextCell;
  3757.         }
  3758.     } else {
  3759.         //TODO: WHAT TO DO IF WE DON'T FIND A CELL?
  3760.         // Only case this should fail is if number of cells in this row
  3761.         //   is less than the column being deleted
  3762.         //   So doing nothing is probably OK
  3763.         XP_TRACE(("No cell in column at X = %d", X));
  3764.     }
  3765. }
  3766.  
  3767. CEditTableCellElement* CEditTableRowElement::FindCell(int32 X, XP_Bool bIncludeRightEdge)
  3768. {
  3769.     CEditTableCellElement* pCell = NULL;
  3770.  
  3771.     for ( CEditElement* pChild = GetChild();
  3772.             pChild;
  3773.                 pChild = pChild->GetNextSibling()) 
  3774.     {
  3775.         if ( pChild->IsTableCell() )
  3776.         {
  3777.             int32 cellX = pChild->TableCell()->GetX();
  3778.             int32 cellRight = cellX + pChild->TableCell()->GetWidth();
  3779.  
  3780.             // Back up 1 pixel if we shouldn't match the right edge
  3781.             if( !bIncludeRightEdge )
  3782.                 cellRight--;
  3783.  
  3784.             if ( X >= cellX && X <= cellRight )
  3785.             {
  3786.                 if( pCell )
  3787.                 {
  3788.                     return pChild->TableCell();
  3789.                 } else {
  3790.                     // First time we find a cell
  3791.                     // Save it but continue to see if next cell's
  3792.                     //   left edge matches - use that instead if it does
  3793.                     // (This nonsense is needed for 0 borders where
  3794.                     //   right edge of one cell = left edge of next)
  3795.                     pCell = pChild->TableCell();
  3796.                 }
  3797.             }
  3798.         }
  3799.     }
  3800.     return pCell;
  3801. }
  3802.  
  3803. void CEditTableRowElement::SetData( EDT_TableRowData *pData ){
  3804.     char *pNew = 0;
  3805.     if( pData->align != ED_ALIGN_DEFAULT ){
  3806.         pNew = PR_sprintf_append( pNew, "ALIGN=%s ", EDT_AlignString(pData->align) );
  3807.     }
  3808.  
  3809.     if( pData->valign != ED_ALIGN_DEFAULT ){
  3810.         pNew = PR_sprintf_append( pNew, "VALIGN=%s ", EDT_AlignString(pData->valign) );
  3811.     }
  3812.  
  3813.     if ( pData->pColorBackground ) {
  3814.         SetBackgroundColor(EDT_LO_COLOR(pData->pColorBackground));
  3815.         pNew = PR_sprintf_append( pNew, "BGCOLOR=\"#%06lX\" ", GetBackgroundColor().GetAsLong() );
  3816.     }
  3817.     else {
  3818.         SetBackgroundColor(ED_Color::GetUndefined());
  3819.     }
  3820.  
  3821.     if ( pData->pBackgroundImage ) {
  3822.         SetBackgroundImage(pData->pBackgroundImage);
  3823.         pNew = PR_sprintf_append( pNew, "BACKGROUND=\"%s\" ", pData->pBackgroundImage );
  3824.     }
  3825.     else {
  3826.         SetBackgroundImage(0);
  3827.     }
  3828.     if ( pData->bBackgroundNoSave) {
  3829.         pNew = PR_sprintf_append( pNew, "NOSAVE " );
  3830.     }
  3831.  
  3832.     if( pData->pExtra  ){
  3833.         pNew = PR_sprintf_append( pNew, "%s ", pData->pExtra );
  3834.     }
  3835.  
  3836.     if( pNew ){
  3837.         pNew = PR_sprintf_append( pNew, ">" );
  3838.     }
  3839.     SetTagData( pNew );
  3840.     if ( pNew ) {
  3841.         free(pNew);
  3842.     }
  3843. }
  3844.  
  3845. EDT_TableRowData* CEditTableRowElement::GetData( ){
  3846.     EDT_TableRowData *pRet;
  3847.     PA_Tag* pTag = TagOpen(0);
  3848.     pRet = ParseParams( pTag, GetWinCSID() );
  3849.     PA_FreeTag( pTag );
  3850.     return pRet;
  3851. }
  3852.  
  3853. static char *tableRowParams[] = {
  3854.     PARAM_ALIGN,
  3855.     PARAM_VALIGN,
  3856.     PARAM_BGCOLOR,
  3857.     PARAM_BACKGROUND,
  3858.     PARAM_NOSAVE,
  3859.     0
  3860. };
  3861.  
  3862. EDT_TableRowData* CEditTableRowElement::ParseParams( PA_Tag *pTag, int16 csid ){
  3863.     EDT_TableRowData* pData = NewData();
  3864.     ED_Alignment align;
  3865.     
  3866.     align = edt_FetchParamAlignment( pTag, ED_ALIGN_DEFAULT, FALSE, csid );
  3867.     if( align == ED_ALIGN_RIGHT || align == ED_ALIGN_LEFT || align == ED_ALIGN_ABSCENTER ){
  3868.         pData->align = align;
  3869.     }
  3870.     align = edt_FetchParamAlignment( pTag, ED_ALIGN_DEFAULT, TRUE, csid );
  3871.     if( align == ED_ALIGN_ABSTOP || align == ED_ALIGN_ABSBOTTOM || align == ED_ALIGN_ABSCENTER
  3872.         || align == ED_ALIGN_BASELINE ){
  3873.         pData->valign = align;
  3874.     }
  3875.     pData->pColorBackground = edt_MakeLoColor(edt_FetchParamColor( pTag, PARAM_BGCOLOR, csid ));
  3876.     pData->pBackgroundImage = edt_FetchParamString( pTag, PARAM_BACKGROUND, csid );
  3877.     pData->bBackgroundNoSave = edt_FetchParamBoolExist( pTag, PARAM_NOSAVE, csid );
  3878.     pData->pExtra = edt_FetchParamExtras( pTag, tableRowParams, csid );
  3879.  
  3880.     return pData;
  3881. }
  3882.  
  3883. EDT_TableRowData* CEditTableRowElement::NewData(){
  3884.     EDT_TableRowData* pData = XP_NEW( EDT_TableRowData );
  3885.     if( pData == 0 ){
  3886.         // throw();
  3887.         return pData;
  3888.     }
  3889.     pData->align = ED_ALIGN_DEFAULT;
  3890.     pData->valign = ED_ALIGN_TOP; // ED_ALIGN_DEFAULT; this is centered - stupid!
  3891.     pData->pColorBackground = 0;
  3892.     pData->pBackgroundImage = 0;
  3893.     pData->bBackgroundNoSave = FALSE;
  3894.     pData->pExtra = 0;
  3895.     return pData;
  3896. }
  3897.  
  3898. void CEditTableRowElement::FreeData( EDT_TableRowData *pData ){
  3899.     if( pData->pColorBackground ) XP_FREE( pData->pColorBackground );
  3900.     if( pData->pBackgroundImage ) XP_FREE( pData->pBackgroundImage );
  3901.     if( pData->pExtra ) XP_FREE( pData->pExtra );
  3902.     XP_FREE( pData );
  3903. }
  3904.  
  3905. void CEditTableRowElement::SetBackgroundColor( ED_Color iColor ){
  3906.     m_backgroundColor = iColor;
  3907. }
  3908.  
  3909. ED_Color CEditTableRowElement::GetBackgroundColor(){
  3910.     return m_backgroundColor;
  3911. }
  3912.  
  3913. void CEditTableRowElement::SetBackgroundImage( char* pImage ){
  3914.     delete m_pBackgroundImage;
  3915.     m_pBackgroundImage = 0;
  3916.     if ( pImage ) {
  3917.         m_pBackgroundImage = XP_STRDUP(pImage);
  3918.     }
  3919. }
  3920.  
  3921. char* CEditTableRowElement::GetBackgroundImage(){
  3922.     return m_pBackgroundImage;
  3923. }
  3924.  
  3925. // class CEditCaptionElement
  3926.  
  3927. CEditCaptionElement::CEditCaptionElement()
  3928.     : CEditSubDocElement((CEditElement*) NULL, P_CAPTION)
  3929. {
  3930. }
  3931.  
  3932. CEditCaptionElement::CEditCaptionElement(CEditElement *pParent, PA_Tag *pTag, int16 /*csid*/)
  3933.     : CEditSubDocElement(pParent, P_CAPTION)
  3934. {
  3935.     if( pTag ){
  3936.         char *locked_buff;
  3937.             
  3938.         PA_LOCK(locked_buff, char *, pTag->data );
  3939.         if( locked_buff ){
  3940.             SetTagData( locked_buff );
  3941.         }
  3942.         PA_UNLOCK(pTag->data);
  3943.     }
  3944. }
  3945.  
  3946. CEditCaptionElement::CEditCaptionElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer)
  3947.     : CEditSubDocElement(pStreamIn, pBuffer)
  3948. {
  3949. }
  3950.  
  3951. void CEditCaptionElement::SetData( EDT_TableCaptionData *pData ){
  3952.     char *pNew = 0;
  3953.     if( pData->align == ED_ALIGN_ABSBOTTOM ){
  3954.         pNew = PR_sprintf_append( pNew, "ALIGN=%s ", EDT_AlignString(pData->align) );
  3955.     }
  3956.     if( pData->pExtra  ){
  3957.         pNew = PR_sprintf_append( pNew, "%s ", pData->pExtra );
  3958.     }
  3959.  
  3960.     if( pNew ){
  3961.         pNew = PR_sprintf_append( pNew, ">" );
  3962.     }
  3963.     SetTagData( pNew );
  3964.     if ( pNew ) {
  3965.         free(pNew);
  3966.     }
  3967. }
  3968.  
  3969. EDT_TableCaptionData* CEditCaptionElement::GetData( ){
  3970.     return GetData( GetWinCSID() );
  3971. }
  3972.  
  3973. EDT_TableCaptionData* CEditCaptionElement::GetData( int16 csid ){
  3974.     EDT_TableCaptionData* pRet;
  3975.     PA_Tag* pTag = TagOpen(0);
  3976.     pRet = ParseParams( pTag, csid );
  3977.     PA_FreeTag( pTag );
  3978.     return pRet;
  3979. }
  3980.  
  3981. static char *tableCaptionParams[] = {
  3982.     PARAM_ALIGN,
  3983.     0
  3984. };
  3985.  
  3986. EDT_TableCaptionData* CEditCaptionElement::ParseParams( PA_Tag *pTag, int16 csid ){
  3987.     EDT_TableCaptionData* pData = NewData();
  3988.     pData->align = edt_FetchParamAlignment( pTag, ED_ALIGN_ABSTOP, FALSE, csid );
  3989.     pData->pExtra = edt_FetchParamExtras( pTag, tableCaptionParams, csid );
  3990.     return pData;
  3991. }
  3992.  
  3993. EDT_TableCaptionData* CEditCaptionElement::NewData(){
  3994.     EDT_TableCaptionData* pData = XP_NEW( EDT_TableCaptionData );
  3995.     if( pData == 0 ){
  3996.         // throw();
  3997.         return pData;
  3998.     }
  3999.     pData->align = ED_ALIGN_CENTER;
  4000.     pData->pExtra = 0;
  4001.     return pData;
  4002. }
  4003.  
  4004. void CEditCaptionElement::FreeData( EDT_TableCaptionData *pData ){
  4005.     if( pData->pExtra ) XP_FREE( pData->pExtra );
  4006.     XP_FREE( pData );
  4007. }
  4008.  
  4009. // class CEditTableCellElement
  4010.  
  4011. CEditTableCellElement::CEditTableCellElement()
  4012.     : CEditSubDocElement((CEditElement*) NULL, P_TABLE_DATA),
  4013.       m_backgroundColor(),
  4014.       m_iBackgroundSaveIndex(0),
  4015.       m_pBackgroundImage(0),
  4016.       m_iWidth(1),
  4017.       m_iWidthPixels(1),
  4018.       m_iHeight(1),
  4019.       m_iHeightPixels(1),
  4020.       m_bWidthPercent(FALSE),
  4021.       m_bHeightPercent(FALSE),
  4022.       m_iColSpan(1),
  4023.       m_iRowSpan(1),
  4024.       m_iRow(0),
  4025.       m_X(0),
  4026.       m_Y(0),
  4027.       m_pSaveParent(0),
  4028.       m_pSaveNext(0)
  4029. {
  4030. }
  4031.  
  4032. CEditTableCellElement::CEditTableCellElement(XP_Bool bIsHeader)
  4033.     : CEditSubDocElement((CEditElement*) NULL, bIsHeader ? P_TABLE_HEADER : P_TABLE_DATA),
  4034.       m_backgroundColor(),
  4035.       m_iBackgroundSaveIndex(0),
  4036.       m_pBackgroundImage(0),
  4037.       m_iWidth(1),
  4038.       m_iWidthPixels(1),
  4039.       m_iHeight(1),
  4040.       m_iHeightPixels(1),
  4041.       m_bWidthPercent(FALSE),
  4042.       m_bHeightPercent(FALSE),
  4043.       m_iColSpan(1),
  4044.       m_iRowSpan(1),
  4045.       m_iRow(0),
  4046.       m_X(0),
  4047.       m_Y(0),
  4048.       m_pSaveParent(0),
  4049.       m_pSaveNext(0)
  4050. {
  4051. }
  4052.  
  4053. CEditTableCellElement::CEditTableCellElement(CEditElement *pParent, PA_Tag *pTag, int16 /*csid*/)
  4054.     : CEditSubDocElement(pParent, pTag->type),
  4055.       m_backgroundColor(),
  4056.       m_iBackgroundSaveIndex(0),
  4057.       m_pBackgroundImage(0),
  4058.       m_iWidth(1),
  4059.       m_iWidthPixels(1),
  4060.       m_iHeight(1),
  4061.       m_iHeightPixels(1),
  4062.       m_bWidthPercent(FALSE),
  4063.       m_bHeightPercent(FALSE),
  4064.       m_iColSpan(1),
  4065.       m_iRowSpan(1),
  4066.       m_iRow(0),
  4067.       m_X(0),
  4068.       m_Y(0),
  4069.       m_pSaveParent(0),
  4070.       m_pSaveNext(0)
  4071. {
  4072.     if( pTag ){
  4073.         char *locked_buff;
  4074.             
  4075.         PA_LOCK(locked_buff, char *, pTag->data );
  4076.         if( locked_buff ){
  4077.             SetTagData( locked_buff );
  4078.         }
  4079.         PA_UNLOCK(pTag->data);
  4080.     }
  4081. }
  4082.  
  4083. CEditTableCellElement::CEditTableCellElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer)
  4084.     : CEditSubDocElement(pStreamIn, pBuffer),
  4085.       m_backgroundColor(),
  4086.       m_iBackgroundSaveIndex(0),
  4087.       m_pBackgroundImage(0),
  4088.       m_iWidth(1),
  4089.       m_iWidthPixels(1),
  4090.       m_iHeight(1),
  4091.       m_iHeightPixels(1),
  4092.       m_bWidthPercent(FALSE),
  4093.       m_bHeightPercent(FALSE),
  4094.       m_iColSpan(1),
  4095.       m_iRowSpan(1),
  4096.       m_iRow(0),
  4097.       m_X(0),
  4098.       m_Y(0),
  4099.       m_pSaveParent(0),
  4100.       m_pSaveNext(0)
  4101. {
  4102.     // Get width from stream -- needed for pasting tables/cells
  4103.     // We must get CSID from buffer since element is not in doc yet
  4104.     //TODO: Should we pass in CSID from paste stream instead?
  4105.     PA_Tag* pTag = CEditElement::TagOpen(0);
  4106.     EDT_TableCellData *pData = ParseParams( pTag, pBuffer->GetRAMCharSetID() );
  4107.     if(pData)
  4108.     {
  4109.         m_iWidth = pData->iWidth;
  4110.         m_bWidthPercent = pData->bWidthPercent;
  4111.         if( !m_bWidthPercent )
  4112.             m_iWidthPixels = m_iWidth;
  4113.  
  4114.         m_iHeight = pData->iHeight;
  4115.         m_bHeightPercent = pData->bHeightPercent;
  4116.         if( !m_bHeightPercent )
  4117.             m_iHeightPixels = m_iHeight;
  4118.  
  4119.         EDT_FreeTableCellData(pData);
  4120.     }
  4121.     PA_FreeTag( pTag );
  4122. }
  4123.  
  4124. CEditTableCellElement::~CEditTableCellElement(){
  4125.     delete m_pBackgroundImage;
  4126. }
  4127.  
  4128. XP_Bool CEditTableCellElement::IsTableCell(){
  4129.     return TRUE;
  4130. }
  4131.  
  4132. EEditElementType CEditTableCellElement::GetElementType(){
  4133.     return eTableCellElement;
  4134. }
  4135.  
  4136. ED_Alignment CEditTableCellElement::GetDefaultAlignment(){
  4137.     return ED_ALIGN_DEFAULT;
  4138. /*
  4139.     EDT_TableCellData* pData = GetData();
  4140.     ED_Alignment result = IsTableData() ? ED_ALIGN_LEFT : ED_ALIGN_ABSCENTER;
  4141.     if ( pData->align != ED_ALIGN_DEFAULT ) {
  4142.         result = pData->align;
  4143.     }
  4144.     FreeData(pData);
  4145.     return result;
  4146. */
  4147. }
  4148.  
  4149. XP_Bool CEditTableCellElement::IsTableData(){
  4150.     return GetType() == P_TABLE_DATA;
  4151. }
  4152.  
  4153. void CEditTableCellElement::IncreaseColSpan(int32 iIncrease)
  4154. {
  4155.     EDT_TableCellData *pData = GetData();
  4156.     if( pData )
  4157.     {
  4158.         pData->iColSpan += iIncrease;
  4159.         SetData(pData);
  4160.         EDT_FreeTableCellData(pData);
  4161.     }
  4162. }
  4163.  
  4164. void CEditTableCellElement::IncreaseRowSpan(int32 iIncrease)
  4165. {
  4166.     EDT_TableCellData *pData = GetData();
  4167.     if( pData )
  4168.     {
  4169.         pData->iRowSpan += iIncrease;
  4170.         SetData(pData);
  4171.         EDT_FreeTableCellData(pData);
  4172.     }
  4173. }
  4174.  
  4175. void CEditTableCellElement::DecreaseColSpan(int32 iDecrease)
  4176. {
  4177.     EDT_TableCellData *pData = GetData();
  4178.     if( pData )
  4179.     {
  4180.         pData->iColSpan = max(1, pData->iColSpan - iDecrease);
  4181.         SetData(pData);
  4182.         EDT_FreeTableCellData(pData);
  4183.     }
  4184. }
  4185.  
  4186. void CEditTableCellElement::DecreaseRowSpan(int32 iDecrease)
  4187. {
  4188.     EDT_TableCellData *pData = GetData();
  4189.     if( pData )
  4190.     {
  4191.         pData->iRowSpan = max(1, pData->iRowSpan - iDecrease);
  4192.         SetData(pData);
  4193.         EDT_FreeTableCellData(pData);
  4194.     }
  4195. }
  4196.  
  4197. void CEditTableCellElement::SetData( EDT_TableCellData *pData ){
  4198.     // bHeader is actually stored as the tag data
  4199.     if ( pData->bHeader ) {
  4200.         SetType(P_TABLE_HEADER);
  4201.     }
  4202.     else {
  4203.         SetType(P_TABLE_DATA);
  4204.     }
  4205.     //TODO: Account for interaction between default align and Header style
  4206.     char *pNew = 0;
  4207.     if( pData->align != ED_ALIGN_DEFAULT ){
  4208.         pNew = PR_sprintf_append( pNew, "ALIGN=%s ", EDT_AlignString(pData->align) );
  4209.     }
  4210.     if( pData->valign != ED_ALIGN_DEFAULT ){
  4211.         pNew = PR_sprintf_append( pNew, "VALIGN=%s ", EDT_AlignString(pData->valign) );
  4212.     }
  4213.     if ( pData->iColSpan != 1 ){
  4214.         pNew = PR_sprintf_append( pNew, "COLSPAN=\"%d\" ", pData->iColSpan );
  4215.     }
  4216.     if ( pData->iRowSpan != 1 ){
  4217.         pNew = PR_sprintf_append( pNew, "ROWSPAN=\"%d\" ", pData->iRowSpan );
  4218.     }
  4219.     if ( pData->bNoWrap ){
  4220.         pNew = PR_sprintf_append( pNew, "NOWRAP " );
  4221.     }
  4222.     if( pData->bWidthDefined ){
  4223.         if( pData->bWidthPercent ){
  4224.             pNew = PR_sprintf_append( pNew, "WIDTH=\"%ld%%\" ", 
  4225.                                       (long)min(100,max(1,pData->iWidth)) );
  4226.         }
  4227.         else {
  4228.             pNew = PR_sprintf_append( pNew, "WIDTH=\"%ld\" ", (long)pData->iWidth );
  4229.         }
  4230.     }
  4231.     if( pData->bHeightDefined ){
  4232.         if( pData->bHeightPercent ){
  4233.             pNew = PR_sprintf_append( pNew, "HEIGHT=\"%ld%%\" ", 
  4234.                                       (long)min(100,max(1, pData->iHeight)) );
  4235.         }
  4236.         else {
  4237.             pNew = PR_sprintf_append( pNew, "HEIGHT=\"%ld\" ", (long)pData->iHeight );
  4238.         }
  4239.     }
  4240.  
  4241.     if ( pData->pColorBackground ) {
  4242.         SetBackgroundColor(EDT_LO_COLOR(pData->pColorBackground));
  4243.         pNew = PR_sprintf_append( pNew, "BGCOLOR=\"#%06lX\" ", GetBackgroundColor().GetAsLong() );
  4244.     }
  4245.     else {
  4246.         SetBackgroundColor(ED_Color::GetUndefined());
  4247.     }
  4248.  
  4249.     if ( pData->pBackgroundImage ) {
  4250.         SetBackgroundImage(pData->pBackgroundImage);
  4251.         pNew = PR_sprintf_append( pNew, "BACKGROUND=\"%s\" ", pData->pBackgroundImage );
  4252.     }
  4253.     else {
  4254.         SetBackgroundImage(0);
  4255.     }
  4256.     if ( pData->bBackgroundNoSave) {
  4257.         pNew = PR_sprintf_append( pNew, "NOSAVE " );
  4258.     }
  4259.  
  4260.     if( pData->pExtra  ){
  4261.         pNew = PR_sprintf_append( pNew, "%s ", pData->pExtra );
  4262.     }
  4263.  
  4264.     if( pNew ){
  4265.         pNew = PR_sprintf_append( pNew, ">" );
  4266.     }
  4267.     SetTagData( pNew );
  4268.     if ( pNew ) {
  4269.         free(pNew);
  4270.     }
  4271.  
  4272.     // Save these since they are not saved in tag data if bWidthPercent = FALSE
  4273.     m_X = pData->X;
  4274.     m_Y = pData->Y;
  4275.  
  4276.     m_iWidthPixels = pData->iWidthPixels;
  4277.     m_iHeightPixels = pData->iHeightPixels;
  4278.  
  4279.     m_iWidth = pData->iWidth;
  4280.     m_bWidthPercent = pData->bWidthPercent;
  4281.  
  4282.     m_iHeight = pData->iHeight;
  4283.     m_bHeightPercent = pData->bHeightPercent;
  4284.  
  4285.     m_iRow = pData->iRow;
  4286.     m_iColSpan = pData->iColSpan;
  4287.     m_iRowSpan = pData->iRowSpan;
  4288. }
  4289.  
  4290. EDT_TableCellData* CEditTableCellElement::GetData( int16 csid )
  4291. {
  4292.     //TODO: Account for interaction between default align and Header style
  4293.     EDT_TableCellData *pRet;
  4294.     PA_Tag* pTag = TagOpen(0);
  4295.     // Must supply csid if cell is not already in the document
  4296.     if( csid == 0 )
  4297.         csid = GetWinCSID();
  4298.  
  4299.     pRet = ParseParams( pTag, csid );
  4300.     PA_FreeTag( pTag );
  4301.     // Return the actual X, Y, width, and height
  4302.     //    as determined by table layout algorithm
  4303.     //   This is set correctly event if bWidthDefined = FALSE
  4304.     pRet->X = m_X;
  4305.     pRet->Y = m_Y;
  4306.     pRet->iRow = m_iRow;
  4307.     pRet->iWidth = m_iWidth;
  4308.     pRet->iWidthPixels = m_iWidthPixels;
  4309.     pRet->iHeight = m_iHeight;
  4310.     pRet->iHeightPixels = m_iHeightPixels;
  4311.  
  4312.     pRet->iRow = m_iRow;
  4313.     pRet->iColSpan = m_iColSpan;
  4314.     pRet->iRowSpan = m_iRowSpan;
  4315.     pRet->mask = -1;
  4316.     pRet->iSelectionType = ED_HIT_NONE;
  4317.     pRet->iSelectedCount = 0;
  4318.     return pRet;
  4319. }
  4320.  
  4321. static XP_Bool edt_CompareLoColors( LO_Color *pLoColor1, LO_Color *pLoColor2 )
  4322. {
  4323.     // One or the other doesn't have color,
  4324.     //  they are different
  4325.     if( (!pLoColor1 && pLoColor2) ||
  4326.         (pLoColor1 && !pLoColor2 ) )
  4327.     {
  4328.         return FALSE;
  4329.     }
  4330.     // Neither has a color - the same
  4331.     if( !pLoColor1 && !pLoColor2 )
  4332.         return TRUE;
  4333.  
  4334.     // Any color is different
  4335.     if( pLoColor1->red != pLoColor2->red ||
  4336.         pLoColor1->green != pLoColor2->green ||
  4337.         pLoColor1->blue != pLoColor2->blue )
  4338.     {
  4339.         return FALSE;
  4340.     }
  4341.     // All must be the same
  4342.     return TRUE;
  4343. }
  4344.  
  4345. static XP_Bool edt_CompareStrings( char *pString1, char *pString2, XP_Bool bFilenameCompare )
  4346. {
  4347.     // One or the other doesn't exist,
  4348.     //  they are different
  4349.     if( (!pString1 && pString2) ||
  4350.         (pString1 && !pString2 ) )
  4351.     {
  4352.         return FALSE;
  4353.     }
  4354.     // Neither exists - the same
  4355.     if( !pString1 && !pString2 )
  4356.     {
  4357.         return TRUE;
  4358.     }
  4359.     
  4360.     // Compare the strings
  4361.     if( bFilenameCompare )
  4362.     {
  4363.         // This compare pays NO attention to case differences for Windows only
  4364.         return (0 == XP_FILENAMECMP(pString1, pString2));
  4365.     }
  4366.  
  4367.     return (0 == XP_STRCMP(pString1, pString2));
  4368. }
  4369.  
  4370. void CEditTableCellElement::MaskData( EDT_TableCellData *pData )
  4371. {
  4372.     if( !pData )
  4373.         return;
  4374.     
  4375.     EDT_TableCellData *pCurrentData = GetData();
  4376.  
  4377.     // Change data only for attributes whose bit is set in data mask
  4378.     if( (pData->mask & CF_ALIGN) &&
  4379.         pCurrentData->align != pData->align )
  4380.     {
  4381.         pData->mask &= ~CF_ALIGN;
  4382.     }
  4383.  
  4384.     if( (pData->mask & CF_VALIGN) &&
  4385.         pCurrentData->valign != pData->valign )
  4386.     {
  4387.         pData->mask &= ~CF_VALIGN;
  4388.     }
  4389.  
  4390.     if( (pData->mask & CF_COLSPAN) &&
  4391.         pCurrentData->iColSpan != pData->iColSpan )
  4392.     {
  4393.         pData->mask &= ~CF_COLSPAN;
  4394.     }
  4395.  
  4396.     if( (pData->mask & CF_ROWSPAN) &&
  4397.         pCurrentData->iRowSpan != pData->iRowSpan )
  4398.     {
  4399.         pData->mask &= ~CF_ROWSPAN;
  4400.     }
  4401.  
  4402.     if( (pData->mask & CF_HEADER) &&
  4403.         pCurrentData->bHeader != pData->bHeader )
  4404.     {
  4405.         pData->mask &= ~CF_HEADER;
  4406.     }
  4407.  
  4408.     if( (pData->mask & CF_NOWRAP) &&
  4409.         pCurrentData->bNoWrap != pData->bNoWrap )
  4410.     {
  4411.         pData->mask &= ~CF_NOWRAP;
  4412.     }
  4413.  
  4414.     if( (pData->mask & CF_BACK_NOSAVE) &&
  4415.         pCurrentData->bBackgroundNoSave != pData->bBackgroundNoSave )
  4416.     {
  4417.         pData->mask &= ~CF_BACK_NOSAVE;
  4418.     }
  4419.  
  4420.     if( (pData->mask & CF_WIDTH) &&
  4421.         (pCurrentData->bWidthDefined != pData->bWidthDefined ||
  4422.          pCurrentData->iWidth != pData->iWidth ||
  4423.          pCurrentData->bWidthPercent != pData->bWidthPercent) )
  4424.     {
  4425.         pData->mask &= ~CF_WIDTH;
  4426.     }
  4427.  
  4428.     if( (pData->mask & CF_HEIGHT) &&
  4429.         (pCurrentData->bHeightDefined != pData->bHeightDefined ||
  4430.          pCurrentData->iHeight != pData->iHeight ||
  4431.          pCurrentData->bHeightPercent != pData->bHeightPercent) )
  4432.     {
  4433.         pData->mask &= ~CF_HEIGHT;
  4434.     }
  4435.  
  4436.     if( (pData->mask & CF_BACK_COLOR) &&
  4437.          !edt_CompareLoColors(pCurrentData->pColorBackground, pData->pColorBackground) )
  4438.     {
  4439.         pData->mask &= ~CF_BACK_COLOR;
  4440.     }
  4441.  
  4442.     // This pays NO attention to case differences for Windows only
  4443.     if( (pData->mask & CF_BACK_IMAGE) &&
  4444.         !edt_CompareStrings(pCurrentData->pBackgroundImage, pData->pBackgroundImage, TRUE) )
  4445.     {
  4446.         pData->mask &= ~CF_BACK_IMAGE;
  4447.     }
  4448.  
  4449.     // This pays attention to case differences
  4450.     if( (pData->mask & CF_EXTRA_HTML) &&
  4451.         !edt_CompareStrings(pCurrentData->pExtra, pData->pExtra, FALSE) )
  4452.     {
  4453.         pData->mask &= ~CF_EXTRA_HTML;
  4454.     }
  4455. }
  4456.  
  4457. // Add up widths of all cells in the first row of table
  4458. // This is available total width for "Percent of Table" calculation
  4459. int32 CEditTableCellElement::GetParentWidth()
  4460. {
  4461.     // Don't return 0 to avoid divide by zero errors
  4462.     int32 iMaxWidth = 1;
  4463.  
  4464.     CEditTableElement *pTable = GetParentTable();
  4465.     if( pTable )
  4466.     {
  4467.         CEditTableCellElement *pCell = pTable->GetFirstCell();
  4468.         while( pCell && pCell->IsTableCell() )
  4469.         {
  4470.             iMaxWidth += pCell->GetWidth();
  4471.             pCell = (CEditTableCellElement*)(pCell->GetNextSibling());
  4472.         }
  4473.     }
  4474.     return iMaxWidth;
  4475. }
  4476.  
  4477. int32 CEditTableCellElement::GetParentHeight()
  4478. {
  4479.     // Don't return 0 to avoid divide by zero errors
  4480.     int32 iMaxHeight = 1;
  4481.  
  4482.     CEditTableElement *pTable = GetParentTable();
  4483.     if( pTable )
  4484.     {
  4485.         CEditTableCellElement *pCell = pTable->GetFirstCell();
  4486.         while( pCell )
  4487.         {
  4488.             iMaxHeight += pCell->GetHeight();
  4489.             pCell = pTable->GetNextCellInColumn(pCell);
  4490.         }
  4491.     }
  4492.     return iMaxHeight;
  4493. }
  4494.  
  4495. static char *tableCellParams[] = {
  4496.     PARAM_ALIGN,
  4497.     PARAM_VALIGN,
  4498.     PARAM_COLSPAN,
  4499.     PARAM_ROWSPAN,
  4500.     PARAM_NOWRAP,
  4501.     PARAM_WIDTH,
  4502.     PARAM_HEIGHT,
  4503.     PARAM_BGCOLOR,
  4504.     PARAM_BACKGROUND,
  4505.     PARAM_NOSAVE,
  4506.     0
  4507. };
  4508.  
  4509. EDT_TableCellData* CEditTableCellElement::ParseParams( PA_Tag *pTag, int16 csid ){
  4510.     EDT_TableCellData *pData = NewData();
  4511.     
  4512.     ED_Alignment align;
  4513.     
  4514.     align = edt_FetchParamAlignment( pTag, ED_ALIGN_DEFAULT, FALSE, csid );
  4515.     if( align == ED_ALIGN_RIGHT || align == ED_ALIGN_LEFT || align == ED_ALIGN_ABSCENTER ){
  4516.         pData->align = align;
  4517.     }
  4518.  
  4519.     align = edt_FetchParamAlignment( pTag, ED_ALIGN_DEFAULT, TRUE, csid );
  4520.     if( align == ED_ALIGN_ABSTOP || align == ED_ALIGN_ABSBOTTOM || align == ED_ALIGN_ABSCENTER
  4521.         || align == ED_ALIGN_BASELINE ){
  4522.         pData->valign = align;
  4523.     }
  4524.  
  4525.     pData->iColSpan = edt_FetchParamInt(pTag, PARAM_COLSPAN, 1, csid);
  4526.     pData->iRowSpan = edt_FetchParamInt(pTag, PARAM_ROWSPAN, 1, csid);
  4527.     m_iColSpan = pData->iColSpan;
  4528.     m_iRowSpan = pData->iRowSpan;
  4529.     pData->bHeader = GetType() == P_TABLE_HEADER;
  4530.     pData->bNoWrap = edt_FetchParamBoolExist(pTag, PARAM_NOWRAP, csid );
  4531.  
  4532.     pData->bWidthDefined = edt_FetchDimension( pTag, PARAM_WIDTH, 
  4533.                     &pData->iWidth, &pData->bWidthPercent,
  4534.                     100, TRUE, csid );
  4535.     pData->bHeightDefined = edt_FetchDimension( pTag, PARAM_HEIGHT, 
  4536.                     &pData->iHeight, &pData->bHeightPercent,
  4537.                     100, TRUE, csid );
  4538.  
  4539.     // If width and/or height are NOT defined, use the
  4540.     //    "bPercent" values set by any previous SetData() calls
  4541.     if( !pData->bWidthDefined )
  4542.         pData->bWidthPercent = m_bWidthPercent;
  4543.     if( !pData->bHeightDefined )
  4544.         pData->bHeightPercent = m_bHeightPercent;
  4545.  
  4546.     pData->pColorBackground = edt_MakeLoColor(edt_FetchParamColor( pTag, PARAM_BGCOLOR, csid ));
  4547.     pData->pBackgroundImage = edt_FetchParamString( pTag, PARAM_BACKGROUND, csid );
  4548.     pData->bBackgroundNoSave = edt_FetchParamBoolExist( pTag, PARAM_NOSAVE, csid );
  4549.     pData->pExtra = edt_FetchParamExtras( pTag, tableCellParams, csid );
  4550.     return pData;
  4551. }
  4552.  
  4553. EDT_TableCellData* CEditTableCellElement::NewData(){
  4554.     EDT_TableCellData *pData = XP_NEW( EDT_TableCellData );
  4555.     if( pData == 0 ){
  4556.         // throw();
  4557.         return pData;
  4558.     }
  4559.     pData->align = ED_ALIGN_DEFAULT;
  4560.     pData->valign = ED_ALIGN_ABSTOP; //ED_ALIGN_TOP; // was ED_ALIGN_DEFAULT; this is centered - stupid!
  4561.     pData->iColSpan = 1;
  4562.     pData->iRowSpan = 1;
  4563.     pData->bHeader = FALSE;
  4564.     pData->bNoWrap = FALSE;
  4565.     pData->bWidthDefined = FALSE;
  4566.     pData->bWidthPercent = TRUE;
  4567.     pData->iWidth = 100;
  4568.     pData->X = 0;
  4569.     pData->Y = 0;
  4570.     pData->iRow = 0;
  4571.     pData->iWidthPixels = 1;
  4572.     pData->iHeightPixels = 1;
  4573.     pData->bHeightDefined = FALSE;
  4574.     pData->bHeightPercent = TRUE;
  4575.     pData->iHeight = 100;
  4576.     pData->pColorBackground = 0;
  4577.     pData->pBackgroundImage = 0;
  4578.     pData->bBackgroundNoSave = FALSE;
  4579.     pData->pExtra = 0;
  4580.     pData->mask = -1;
  4581.     pData->iSelectionType = ED_HIT_NONE;
  4582.     pData->iSelectedCount = 0;
  4583.     return pData;
  4584. }
  4585.  
  4586. void CEditTableCellElement::FreeData( EDT_TableCellData *pData ){
  4587.     if( pData->pColorBackground ) XP_FREE( pData->pColorBackground );
  4588.     if( pData->pBackgroundImage ) XP_FREE( pData->pBackgroundImage );
  4589.     if( pData->pExtra ) XP_FREE( pData->pExtra );
  4590.     XP_FREE( pData );
  4591. }
  4592.  
  4593. void CEditTableCellElement::SetBackgroundColor( ED_Color iColor ){
  4594.     m_backgroundColor = iColor;
  4595. }
  4596.  
  4597. ED_Color CEditTableCellElement::GetBackgroundColor(){
  4598.     return m_backgroundColor;
  4599. }
  4600.  
  4601. void CEditTableCellElement::SetBackgroundImage( char* pImage ){
  4602.     delete m_pBackgroundImage;
  4603.     m_pBackgroundImage = 0;
  4604.     if ( pImage ) {
  4605.         m_pBackgroundImage = XP_STRDUP(pImage);
  4606.     }
  4607. }
  4608.  
  4609. char* CEditTableCellElement::GetBackgroundImage(){
  4610.     return m_pBackgroundImage;
  4611. }
  4612.  
  4613. LO_CellStruct* CEditTableCellElement::GetLoCell()
  4614. {
  4615.     // Find the first leaf element in the cell since
  4616.     //  it will have pointer to LO_Element
  4617.     CEditElement *pLeaf = FindNextElement(&CEditElement::FindLeafAll,0 );
  4618.     if( pLeaf )
  4619.     {
  4620.         // Only leaf edit elements have their pointers saved in LO_Elements
  4621.         LO_Element *pLoElement = pLeaf->Leaf()->GetLayoutElement();
  4622.         if( pLoElement )
  4623.         {
  4624.             // Find enclosing LO cell
  4625.             LO_CellStruct *pLoCell = lo_GetParentCell(GetEditBuffer()->m_pContext, pLoElement);
  4626.             // We shoul ALWAYS find a cell
  4627.             XP_ASSERT(pLoCell);
  4628.             return pLoCell;
  4629.         }
  4630.     }
  4631.     return NULL;
  4632. }
  4633.  
  4634. CEditTableCellElement* CEditTableCellElement::GetPreviousCellInTable(intn *pRowCounter)
  4635. {
  4636.     // Simplest case is previous cell in the row
  4637.     CEditElement *pPreviousCell = GetPreviousSibling();
  4638.     if( !pPreviousCell )
  4639.     {
  4640.         // Get the last child cell of the previous row
  4641.         if( GetParent() && GetParent()->GetPreviousSibling() )
  4642.             pPreviousCell = GetParent()->GetPreviousSibling()->GetLastChild(); 
  4643. #ifdef DEBUG
  4644.         if( pPreviousCell ) XP_ASSERT(pPreviousCell->IsTableCell());
  4645. #endif
  4646.         // Tell caller we wrapped to the previous row
  4647.         if( pRowCounter && pPreviousCell )
  4648.             (*pRowCounter)--;
  4649.     }
  4650.     return (CEditTableCellElement*)pPreviousCell;
  4651. }
  4652.  
  4653. CEditTableCellElement* CEditTableCellElement::GetNextCellInTable(intn *pRowCounter)
  4654. {
  4655.     // Simplest case is next cell in the row
  4656.     CEditElement *pNextCell = GetNextSibling();
  4657.     if( !pNextCell )
  4658.     {
  4659.         // Get the first child cell of the next row
  4660.         if( GetParent() && GetParent()->GetNextSibling() )
  4661.             pNextCell = GetParent()->GetNextSibling()->GetChild(); 
  4662. #ifdef DEBUG
  4663.         if( pNextCell ) XP_ASSERT(pNextCell->IsTableCell());
  4664. #endif
  4665.         // Tell caller we wrapped to the next row
  4666.         if( pRowCounter && pNextCell )
  4667.             (*pRowCounter)++;
  4668.     }
  4669.     return (CEditTableCellElement*)pNextCell;
  4670. }
  4671.  
  4672. CEditTableCellElement* CEditTableCellElement::GetNextCellInLogicalRow()
  4673. {
  4674.     CEditElement *pNextCell = GetNextSibling();
  4675.     if( pNextCell && pNextCell->IsTableCell() )
  4676.         return pNextCell->TableCell();
  4677.     
  4678.     return NULL;
  4679. }
  4680.  
  4681. CEditTableCellElement* CEditTableCellElement::GetFirstCellInRow(int32 Y, XP_Bool bSpan)
  4682. {
  4683.     CEditTableElement *pTable = GetParentTable();
  4684.     if( pTable )
  4685.         return pTable->GetFirstCellInRow(Y, bSpan);
  4686.  
  4687.     return NULL;
  4688. }
  4689.  
  4690. CEditTableCellElement* CEditTableCellElement::GetFirstCellInColumn(int32 X, XP_Bool bSpan)
  4691. {
  4692.     CEditTableElement *pTable = GetParentTable();
  4693.     if( pTable )
  4694.         return pTable->GetFirstCellInColumn(X, bSpan);
  4695.  
  4696.     return NULL;
  4697. }
  4698.  
  4699. CEditTableCellElement* CEditTableCellElement::GetNextCellInRow()
  4700. {
  4701.     CEditTableElement *pTable = GetParentTable();
  4702.     if( pTable )
  4703.         return pTable->GetNextCellInRow();
  4704.  
  4705.     return NULL;
  4706. }
  4707.  
  4708. CEditTableCellElement* CEditTableCellElement::GetNextCellInColumn()
  4709. {
  4710.     CEditTableElement *pTable = GetParentTable();
  4711.     if( pTable )
  4712.         return pTable->GetNextCellInColumn();
  4713.  
  4714.     return NULL;
  4715. }
  4716.  
  4717. XP_Bool CEditTableCellElement::IsEmpty()
  4718. {
  4719.     CEditElement *pElement = GetFirstMostChild();
  4720.  
  4721.     // Should never happen, but if it does,
  4722.     //  I guess we can consider it empty!
  4723.     if( !pElement )
  4724.         return TRUE;
  4725.  
  4726.     // We are empty if no more sibling containers
  4727.     //   and cell only has an empty string
  4728.     if( GetChild()->GetNextSibling() == 0 && pElement->IsText() )
  4729.     {
  4730.         char *pText = pElement->Text()->GetText();
  4731.         if( pText == 0 || XP_STRLEN(pText) == 0 )
  4732.         {
  4733.             return TRUE;
  4734.         }
  4735.     }
  4736.     return FALSE;
  4737. }
  4738.  
  4739. // Move all contents of supplied cell into this cell
  4740. void CEditTableCellElement::MergeCells(CEditTableCellElement* pCell)
  4741. {
  4742.     if( !pCell || pCell == this )
  4743.         return;
  4744.  
  4745.     // Don't merge cells with only single, empty string as only element
  4746.     // Just delete the cell to be merged
  4747.     if( !pCell->IsEmpty() )
  4748.     {
  4749.         CEditElement *pLastChild = GetChild();
  4750.         if(!pLastChild)
  4751.             return;
  4752.  
  4753.         // First child container to move
  4754.         CEditElement *pMoveChild = pCell->GetChild();
  4755.  
  4756.         if( IsEmpty() )
  4757.         {
  4758.             // We are moving into an empty cell,
  4759.             //  so delete the single container
  4760.             delete pLastChild;
  4761.             // Make the first element the first 
  4762.             //  container we are moving
  4763.             pMoveChild->SetParent( this );
  4764.             SetChild(pMoveChild);
  4765.             
  4766.             // Setup for moving other containers
  4767.             pLastChild = pMoveChild;
  4768.             pMoveChild = pMoveChild->GetNextSibling();
  4769.         } else {
  4770.             // Insert after last existing child of cell,
  4771.             // There is often only 1 container under each cell,
  4772.             //   except if there are multiple paragraphs or
  4773.             //   cell contents already merged
  4774.             while( pLastChild->GetNextSibling() )
  4775.                 pLastChild = pLastChild->GetNextSibling();
  4776.         }
  4777.  
  4778.         while(pMoveChild)
  4779.         {
  4780.             // Move container by changing appropriate pointers
  4781.             pLastChild->SetNextSibling( pMoveChild );
  4782.             pMoveChild->SetParent( this );
  4783.             pLastChild = pMoveChild;
  4784.             pMoveChild = pMoveChild->GetNextSibling();
  4785.         }
  4786.  
  4787.         // Clear pointer to children just moved
  4788.         pCell->SetChild(0);
  4789.     }
  4790.     delete pCell;
  4791. }
  4792.  
  4793. void CEditTableCellElement::DeleteContents()
  4794. {
  4795.     // Get the first conainer in the cell
  4796.     CEditElement *pChild = GetChild();
  4797.  
  4798.     // Delete all other containers
  4799.     while( pChild )
  4800.     {
  4801.         CEditElement *pNext = pChild->GetNextSibling();
  4802.         pChild->Unlink();
  4803.         delete pChild;
  4804.         pChild = pNext;
  4805.     }
  4806.     // Create a default paragraph container,
  4807.     //  this will set cell as its parent
  4808.     CEditContainerElement *pContainer = CEditContainerElement::NewDefaultContainer(this, ED_ALIGN_DEFAULT);
  4809.     if( pContainer )
  4810.     {
  4811.         // Create empty string as only element so we have 
  4812.         //  a leaf element to place caret at
  4813.         CEditTextElement *pText = new CEditTextElement(pContainer,"");
  4814.     }
  4815. }
  4816.  
  4817. // Insert as last child of supplied parent 
  4818. //   but first save current parent and next pointers
  4819. void CEditTableCellElement::SwitchLinkage(CEditTableRowElement *pParentRow)
  4820. {
  4821.     // Save current location in tree
  4822.     m_pSaveParent = GetParent();
  4823.     m_pSaveNext = GetNextSibling();
  4824. #ifdef DEBUG
  4825.     // This prevents XP_ASSERT when trying to InsertAsLastChild;
  4826.     SetParent(0);
  4827. #endif
  4828.     // Insert into new row
  4829.     InsertAsLastChild((CEditElement*)pParentRow);
  4830. }
  4831.  
  4832. // Switches back to saved parent and next pointers
  4833. void CEditTableCellElement::RestoreLinkage()
  4834. {
  4835.     Unlink();
  4836.     SetParent(m_pSaveParent);
  4837.     SetNextSibling(m_pSaveNext);
  4838.     CEditElement *pParent = GetParent();
  4839. }
  4840.  
  4841. // Platform-specific end-of-line character(s) added to string,
  4842. //  returning in possibly-reallocated buffer that caller must free
  4843. char *edt_AppendEndOfLine(char *pText)
  4844. {
  4845.     //TODO: FIX THIS FOR MAC AND UNIX LINE-END CONVENTIONS
  4846.     if( pText )
  4847.         return PR_sprintf_append( pText, "\r\n" );
  4848.     
  4849.     return NULL;
  4850. }
  4851.  
  4852. char* CEditTableCellElement::GetText(XP_Bool bJoinParagraphs)
  4853. {
  4854.     char *pCellText = NULL;
  4855.     CEditElement *pChild = GetChild();
  4856.  
  4857.     //Prevents adding delimiter before first element
  4858.     XP_Bool bFirst = TRUE;
  4859.  
  4860.     while( pChild )
  4861.     {
  4862.         if( pChild->IsContainer() )
  4863.         {
  4864.             char *pText = pChild->Container()->GetText();
  4865.             if( pText && *pText )
  4866.             {
  4867.                 if( !bFirst )
  4868.                 {
  4869.                     if( bJoinParagraphs )
  4870.                     {
  4871.                         // Add 2 spaces between paragraphs instead of CR/LF
  4872.                         //  so we can merge all contents in table cell
  4873.                         pCellText = PR_sprintf_append( pCellText, "  " );
  4874.                     } else {
  4875.                         pCellText = edt_AppendEndOfLine( pCellText);
  4876.                     }
  4877.                 }                    
  4878.                 pCellText = PR_sprintf_append(pCellText, pText);
  4879.                 XP_FREE(pText);
  4880.  
  4881.                 bFirst = FALSE;
  4882.             }
  4883.         }
  4884.         pChild = pChild->GetNextSibling();
  4885.     }
  4886.     // We must follow the rule that every cell must have 
  4887.     //   at least 1 empty text element in it,
  4888.     //   so return an empty string if nothing found
  4889.     if( !pCellText )
  4890.         return XP_STRDUP("");
  4891.  
  4892.     return pCellText;
  4893. }
  4894.  
  4895.  
  4896. // class CEditLayerElement
  4897.  
  4898. CEditLayerElement::CEditLayerElement()
  4899.     : CEditElement(0, P_META) // P_LAYER, 0)
  4900. {
  4901.     EDT_LayerData* pData = NewData();
  4902.     // pData->iColumns = columns;
  4903.     SetData(pData);
  4904.     FreeData(pData);
  4905. }
  4906.  
  4907. CEditLayerElement::CEditLayerElement(CEditElement *pParent, PA_Tag *pTag, int16 /*csid*/)
  4908.     : CEditElement(pParent, P_META) // P_LAYER)
  4909. {
  4910.     if( pTag ){
  4911.         char *locked_buff;
  4912.             
  4913.         PA_LOCK(locked_buff, char *, pTag->data );
  4914.         if( locked_buff ){
  4915.             SetTagData( locked_buff );
  4916.         }
  4917.         PA_UNLOCK(pTag->data);
  4918.     }
  4919. }
  4920.  
  4921. CEditLayerElement::CEditLayerElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer)
  4922.     : CEditElement(pStreamIn, pBuffer)
  4923. {
  4924. }
  4925.  
  4926. void CEditLayerElement::FinishedLoad( CEditBuffer* pBuffer ){
  4927.     EnsureSelectableSiblings(pBuffer);
  4928.     CEditElement::FinishedLoad(pBuffer);
  4929. }
  4930.  
  4931. void CEditLayerElement::SetData( EDT_LayerData *pData ){
  4932.     char *pNew = 0;
  4933.     /* if ( pData->iColumns > 1){
  4934.         pNew = PR_sprintf_append( pNew, "COLS=%d ", pData->iColumns );
  4935.     } */
  4936.     if( pData->pExtra  ){
  4937.         pNew = PR_sprintf_append( pNew, "%s ", pData->pExtra );
  4938.     }
  4939.     if( pNew ){
  4940.         pNew = PR_sprintf_append( pNew, ">" );
  4941.     }
  4942.     SetTagData( pNew );
  4943.     if ( pNew ) {
  4944.         free(pNew);
  4945.     }
  4946. }
  4947.  
  4948. EDT_LayerData* CEditLayerElement::GetData( ){
  4949.     EDT_LayerData *pRet;
  4950.     PA_Tag* pTag = TagOpen(0);
  4951.     pRet = ParseParams( pTag, GetWinCSID() );
  4952.     PA_FreeTag( pTag );
  4953.     return pRet;
  4954. }
  4955.  
  4956. static char *LayerParams[] = {
  4957.     // PARAM_COLS,
  4958.     0
  4959. };
  4960.  
  4961. EDT_LayerData* CEditLayerElement::ParseParams( PA_Tag *pTag, int16 csid ){
  4962.     EDT_LayerData *pData = NewData();
  4963.     
  4964.     // pData->iColumns = edt_FetchParamInt(pTag, PARAM_COLS, 1);
  4965.     pData->pExtra = edt_FetchParamExtras( pTag, LayerParams, csid );
  4966.  
  4967.     return pData;
  4968. }
  4969.  
  4970. EDT_LayerData* CEditLayerElement::NewData(){
  4971.     EDT_LayerData *pData = XP_NEW( EDT_LayerData );
  4972.     if( pData == 0 ){
  4973.         // throw();
  4974.         return pData;
  4975.     }
  4976.     // pData->iColumns = 1;
  4977.     pData->pExtra = 0;
  4978.     return pData;
  4979. }
  4980.  
  4981. void CEditLayerElement::FreeData( EDT_LayerData *pData ){
  4982.     if( pData->pExtra ) XP_FREE( pData->pExtra );
  4983.     XP_FREE( pData );
  4984. }
  4985.  
  4986. void CEditLayerElement::PrintOpen( CPrintState *pPrintState ){
  4987.     TagType tSave = GetType();
  4988.     SetType( P_LAYER );
  4989.     CEditElement::PrintOpen( pPrintState );
  4990.     SetType( tSave );
  4991. }
  4992.  
  4993. void CEditLayerElement::PrintEnd( CPrintState *pPrintState ){
  4994.     TagType tSave = GetType();
  4995.     SetType( P_LAYER );
  4996.     CEditElement::PrintEnd( pPrintState );
  4997.     SetType( tSave );
  4998. }
  4999.  
  5000. // CEditDivisionElement
  5001.  
  5002. CEditDivisionElement::CEditDivisionElement()
  5003.     : CEditElement(0, P_DIVISION, 0)
  5004. {
  5005.     EDT_DivisionData* pData = NewData();
  5006.     SetData(pData);
  5007.     FreeData(pData);
  5008. }
  5009.  
  5010. CEditDivisionElement::CEditDivisionElement(CEditElement *pParent, PA_Tag *pTag, int16 /*csid*/)
  5011.     : CEditElement(pParent, P_DIVISION)
  5012. {
  5013.     if( pTag ){
  5014.         char *locked_buff;
  5015.             
  5016.         PA_LOCK(locked_buff, char *, pTag->data );
  5017.         if( locked_buff ){
  5018.             SetTagData( locked_buff );
  5019.         }
  5020.         PA_UNLOCK(pTag->data);
  5021.     }
  5022. }
  5023.  
  5024. CEditDivisionElement::CEditDivisionElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer)
  5025.     : CEditElement(pStreamIn, pBuffer)
  5026. {
  5027. }
  5028.  
  5029. XP_Bool CEditDivisionElement::IsDivision() { return TRUE; }
  5030. CEditDivisionElement* CEditDivisionElement::Cast(CEditElement* pElement) {
  5031.     return pElement && pElement->IsDivision() ? (CEditDivisionElement*) pElement : 0; }
  5032.  
  5033. EEditElementType CEditDivisionElement::GetElementType() { return eDivisionElement; }
  5034. XP_Bool CEditDivisionElement::IsAcceptableChild(CEditElement& pChild){
  5035.     return ! pChild.IsLeaf();
  5036. }
  5037.  
  5038. void CEditDivisionElement::SetData( EDT_DivisionData *pData ){
  5039.     char *pNew = 0;
  5040.     if( pData->align != ED_ALIGN_DEFAULT ){
  5041.         pNew = PR_sprintf_append( pNew, "ALIGN=%s ", EDT_AlignString(pData->align) );
  5042.     }
  5043.     if( pData->pExtra  ){
  5044.         pNew = PR_sprintf_append( pNew, "%s ", pData->pExtra );
  5045.     }
  5046.     if( pNew ){
  5047.         pNew = PR_sprintf_append( pNew, ">" );
  5048.     }
  5049.     SetTagData( pNew );
  5050.     if ( pNew ) {
  5051.         free(pNew);
  5052.     }
  5053. }
  5054.  
  5055. EDT_DivisionData* CEditDivisionElement::GetData( ){
  5056.     EDT_DivisionData *pRet;
  5057.     PA_Tag* pTag = TagOpen(0);
  5058.     pRet = ParseParams( pTag, GetWinCSID() );
  5059.     PA_FreeTag( pTag );
  5060.     return pRet;
  5061. }
  5062.  
  5063. ED_Alignment CEditDivisionElement::GetAlignment(){
  5064.     ED_Alignment result = ED_ALIGN_DEFAULT;
  5065.     EDT_DivisionData* pData = GetData();
  5066.     if ( pData ) {
  5067.         result = pData->align;
  5068.         FreeData(pData);
  5069.     }
  5070.     return result;
  5071. }
  5072.  
  5073. void CEditDivisionElement::ClearAlignment(){
  5074.     EDT_DivisionData* pData = GetData();
  5075.     if ( pData ) {
  5076.         pData->align = ED_ALIGN_DEFAULT;
  5077.         SetData(pData);
  5078.         FreeData(pData);
  5079.     }
  5080. }
  5081.  
  5082. XP_Bool CEditDivisionElement::HasData(){
  5083.     // Does this have data other than the ALIGN tag?
  5084.     XP_Bool hasData = FALSE;
  5085.     EDT_DivisionData* pData = GetData();
  5086.     if ( pData ) {
  5087.         if ( pData->pExtra || pData->align != ED_ALIGN_DEFAULT ) {
  5088.             hasData = TRUE;
  5089.         }
  5090.         FreeData(pData);
  5091.     }
  5092.     return hasData;
  5093. }
  5094.  
  5095. static char *DivisionParams[] = {
  5096.     PARAM_ALIGN,
  5097.     0
  5098. };
  5099.  
  5100. EDT_DivisionData* CEditDivisionElement::ParseParams( PA_Tag *pTag, int16 csid ){
  5101.     EDT_DivisionData *pData = NewData();
  5102.     
  5103.     pData->pExtra = edt_FetchParamExtras( pTag, DivisionParams, csid );
  5104.     pData->align = edt_FetchParamAlignment( pTag, ED_ALIGN_DEFAULT, FALSE, csid );
  5105.  
  5106.     return pData;
  5107. }
  5108.  
  5109. EDT_DivisionData* CEditDivisionElement::NewData(){
  5110.     EDT_DivisionData *pData = XP_NEW( EDT_DivisionData );
  5111.     if( pData == 0 ){
  5112.         // throw();
  5113.         return pData;
  5114.     }
  5115.     pData->align = ED_ALIGN_DEFAULT;
  5116.     pData->pExtra = 0;
  5117.     return pData;
  5118. }
  5119.  
  5120. void CEditDivisionElement::FreeData( EDT_DivisionData *pData ){
  5121.     if ( pData ) {
  5122.         XP_FREEIF( pData->pExtra );
  5123.         XP_FREE( pData );
  5124.     }
  5125. }
  5126.  
  5127. void CEditDivisionElement::PrintOpen( CPrintState *pPrintState ){
  5128.     // Don't print <DIV> tag if it is a no-op.
  5129.     if ( HasData() ) {
  5130.         CEditElement::PrintOpen(pPrintState);
  5131.     }
  5132. }
  5133.  
  5134. void CEditDivisionElement::PrintEnd( CPrintState *pPrintState ){
  5135.     // Don't print <DIV> tag if it is a no-op.
  5136.     if ( HasData() ) {
  5137.         CEditElement::PrintEnd(pPrintState);
  5138.     }
  5139. }
  5140.  
  5141.  
  5142. //-----------------------------------------------------------------------------
  5143. // CEditRootDocElement
  5144. //-----------------------------------------------------------------------------
  5145. void CEditRootDocElement::PrintOpen( CPrintState *pPrintState ){
  5146.     m_pBuffer->PrintDocumentHead( pPrintState );
  5147. }
  5148.  
  5149. void CEditRootDocElement::PrintEnd( CPrintState *pPrintState ){
  5150.     m_pBuffer->PrintDocumentEnd( pPrintState );
  5151. }
  5152.  
  5153. void CEditRootDocElement::FinishedLoad( CEditBuffer *pBuffer ){
  5154.     CEditSubDocElement::FinishedLoad(pBuffer);
  5155.     if ( ! GetLastChild() || ! GetLastChild()->IsEndContainer() ) {
  5156.         CEditEndContainerElement* pEndContainer = new CEditEndContainerElement(NULL);
  5157.         // Creating the element automaticly inserts it.
  5158.         (void) new CEditEndElement(pEndContainer);
  5159.         pEndContainer->InsertAsLastChild(this);
  5160.     }
  5161. }
  5162.  
  5163. #ifdef DEBUG
  5164. void CEditRootDocElement::ValidateTree(){
  5165.     CEditElement::ValidateTree();
  5166.     XP_ASSERT(GetParent() == NULL);
  5167.     XP_ASSERT(GetChild() != NULL);
  5168.     XP_ASSERT(GetLastMostChild()->GetElementType() == eEndElement);
  5169. }
  5170.  
  5171. #endif
  5172.  
  5173.  
  5174. //-----------------------------------------------------------------------------
  5175. // CEditLeafElement
  5176. //-----------------------------------------------------------------------------
  5177.  
  5178.  
  5179. XP_Bool CEditLeafElement::PrevPosition(ElementOffset iOffset, 
  5180.                 CEditLeafElement*& pNew, ElementOffset& iNewOffset ){
  5181.  
  5182.     XP_Bool bPreviousElement = FALSE;
  5183.     XP_Bool bMoved = TRUE;
  5184.  
  5185.     iNewOffset = iOffset - 1;
  5186.     pNew = this;
  5187.  
  5188.     if( iNewOffset == 0 ){
  5189.  
  5190.         CEditElement *pPrev = PreviousLeafInContainer();
  5191.         if( pPrev ){
  5192.             if( pPrev->IsLeaf()){
  5193.                 bPreviousElement = TRUE;
  5194.             }
  5195.         }
  5196.     }
  5197.  
  5198.     //
  5199.     // we need to set the position to the end of the previous element.
  5200.     //
  5201.     if( iNewOffset < 0 || bPreviousElement ){
  5202.         CEditLeafElement *pPrev = PreviousLeaf();
  5203.         if( pPrev ){
  5204.             if( pPrev->IsLeaf()){
  5205.                 pNew = pPrev;
  5206.                 iNewOffset = pNew->Leaf()->GetLen();
  5207.             }
  5208.         }
  5209.         else{
  5210.             // no previous element, we are at the beginning of the buffer.
  5211.             iNewOffset = iOffset;
  5212.             bMoved = FALSE;
  5213.         }
  5214.     }
  5215.     return bMoved;
  5216. }
  5217.  
  5218. XP_Bool CEditLeafElement::NextPosition(ElementOffset iOffset, 
  5219.                 CEditLeafElement*& pNew, ElementOffset& iNewOffset ){
  5220.     LO_Element* pRetText;
  5221.     CEditLeafElement *pNext;
  5222.     int iRetPos;
  5223.     XP_Bool retVal = TRUE;
  5224.  
  5225.     pNew = this;
  5226.     iNewOffset = iOffset + 1;
  5227.  
  5228.     //
  5229.     // if the new offset is obviously greater, move past it.  If
  5230.     //  Make sure the layout engine actually layed out the element.
  5231.     //
  5232.     if( iNewOffset > Leaf()->GetLen() ||
  5233.         !Leaf()->GetLOElementAndOffset( iOffset, FALSE, pRetText, iRetPos ))
  5234.     {
  5235.         pNext = NextLeaf();
  5236.         if( pNext ){
  5237.             pNew = pNext;
  5238.             if( pNext->PreviousLeafInContainer() == 0 ){
  5239.                 iNewOffset = 0;
  5240.             }
  5241.             else {
  5242.                 iNewOffset = 1;
  5243.             }
  5244.         }
  5245.         else {
  5246.             iNewOffset = iOffset;
  5247.             retVal = FALSE;
  5248.         }
  5249.     }
  5250.     return retVal;
  5251. }
  5252.  
  5253. void CEditLeafElement::DisconnectLayoutElements(LO_Element* /*pElement*/){
  5254.     // The intention of this code was to make sure that live layout elements
  5255.     // didn't have back-pointers to dead edit elements.
  5256.     //
  5257.     // Unfortunately, if an edit element is cut out of the root,
  5258.     // and then deleted after a relayout,
  5259.     // the layout element will be stale. And that will cause
  5260.     // a Free Memory Read. This isn't usually a problem, except
  5261.     // on NT, where the rolling heap may have already freed the
  5262.     // heap that had the stale layout element. This happens
  5263.     // most frequently when large tables are deleted.
  5264.     //
  5265.     // This code doesn't seem to be needed for correct operation of
  5266.     // Composer. That's a good thing, because it can't be
  5267.     // made to work without a formal set of rules on the
  5268.     // lifetime of edit elements and layout elements.
  5269. #if 0
  5270.     // Clear back pointer out of the edit elements.
  5271.     LO_Element* e = pElement;
  5272.     while ( e && e->lo_any.edit_element == this ){
  5273.         e->lo_any.edit_element = 0;
  5274.         while ( e && e->lo_any.edit_element == 0 ){
  5275.             e = e->lo_any.next;
  5276.         }
  5277.     }
  5278. #endif
  5279. }
  5280.  
  5281. void CEditLeafElement::SetLayoutElementHelper( intn desiredType, LO_Element** pElementHolder,
  5282.                                               intn iEditOffset, intn lo_type, 
  5283.                                               LO_Element* pLoElement){
  5284.     // XP_TRACE(("SetLayoutElementHelper this(0x%08x) iEditOffset(%d) pLoElement(0x%08x)", this, iEditOffset, pLoElement));
  5285.  
  5286.     if( lo_type == desiredType ){
  5287.         // iEditOffset is:
  5288.         // 0 for normal case,
  5289.         // -1 for end of document element.
  5290.         // > 0 for text items after a line wrap.
  5291.         //     Don't disconnect layout elements because this is
  5292.         //     called for each layout element of a wrapped text
  5293.         //     edit element. Since DisconnectLayoutElements goes
  5294.         //     ahead and zeros out all the "next" elements with
  5295.         //     the same back pointer, we end up trashing later
  5296.         //     elements if the elements are set in some order
  5297.         //     other than first-to-last. This happens when just the 2nd line of a two-line
  5298.         //     text element is being relaid out.
  5299.         if ( iEditOffset <= 0 ) {
  5300.             // Don't reset the old element, because functions like lo_BreakOldElement depend upon the
  5301.             // old element having a good value.
  5302.             *pElementHolder = pLoElement;
  5303.         }
  5304.         pLoElement->lo_any.edit_element = this;
  5305.         pLoElement->lo_any.edit_offset = iEditOffset;
  5306.     }
  5307.     else {
  5308.         // We get called back whenever linefeeds are generated. That's so that
  5309.         // breaks can find their linefeed. But it also means we are
  5310.         // called back more often than nescessary.
  5311.     //    XP_TRACE(("  ignoring inconsistent type.\n"));
  5312.     }
  5313. }
  5314.  
  5315. void CEditLeafElement::ResetLayoutElementHelper( LO_Element** pElementHolder, intn iEditOffset, 
  5316.             LO_Element* pLoElement ){
  5317.     // XP_TRACE(("ResetLayoutElementHelper this(0x%08x) iEditOffset(%d) pLoElement(0x%08x)", this, iEditOffset, pLoElement));
  5318.     if ( iEditOffset <= 0 ) {
  5319.         if ( *pElementHolder == pLoElement ){
  5320.             *pElementHolder = NULL;
  5321.         }
  5322.         else {
  5323.             XP_TRACE(("  Woops -- we're currently pointing to 0x%08x\n", *pElementHolder));
  5324.         }
  5325.     }
  5326. }
  5327.  
  5328. CEditElement* CEditLeafElement::Divide(int iOffset){
  5329.     CEditElement* pNext;
  5330.     if( iOffset == 0 ){
  5331.         return this;
  5332.     }
  5333.  
  5334.     pNext = LeafInContainerAfter();
  5335.     if( iOffset >= GetLen() ){
  5336.         if( pNext != 0 ){
  5337.             return pNext;
  5338.         }
  5339.         else {
  5340.             CEditTextElement* pPrev;
  5341.             CEditTextElement* pNew;
  5342.             if( IsA( P_TEXT )) {
  5343.                 pPrev = this->Text();
  5344.             }
  5345.             else {
  5346.                 pPrev = PreviousTextInContainer();
  5347.             }
  5348.  
  5349.             if( pPrev ){
  5350.                 pNew = pPrev->CopyEmptyText();
  5351.             }
  5352.             else {
  5353.                 pNew = new CEditTextElement((CEditElement*)0,0);
  5354.             }
  5355.             pNew->InsertAfter(this);
  5356.             return pNew;
  5357.         }
  5358.     }
  5359.  
  5360.     // offset is in the middle somewhere Must be a TEXT.
  5361.     XP_ASSERT( IsA(P_TEXT) );
  5362.     CEditTextElement* pNew = Text()->CopyEmptyText();
  5363.     pNew->SetText( Text()->GetText()+iOffset );
  5364.     Text()->GetText()[iOffset] = 0;
  5365.     
  5366.     /* update the text block with the new text */
  5367.     CEditTextElement * pText = Text();
  5368.     if ( pText->m_pTextBlock != NULL ){
  5369.         lo_ChangeText ( pText->m_pTextBlock, pText->GetTextWithConvertedSpaces() );
  5370.     }
  5371.  
  5372.     pNew->InsertAfter(this);
  5373.     return pNew;
  5374. }
  5375.  
  5376. CEditTextElement* CEditLeafElement::CopyEmptyText(CEditElement *pParent){
  5377.        return new CEditTextElement(pParent,0);
  5378. }
  5379.  
  5380. CPersistentEditInsertPoint CEditLeafElement::GetPersistentInsertPoint(ElementOffset offset){
  5381.     ElementIndex len = GetPersistentCount();
  5382.     if ( offset > len )
  5383.     {
  5384. #ifdef DEBUG_EDIT_LAYOUT
  5385.         XP_ASSERT(FALSE);
  5386. #endif
  5387.         offset = len;
  5388.     }
  5389.     return CPersistentEditInsertPoint(GetElementIndex() + offset, offset == 0);
  5390. }
  5391.  
  5392. XP_Bool CEditLeafElement::IsUnknownHTML(){
  5393.     return FALSE;
  5394. }
  5395.  
  5396. XP_Bool CEditLeafElement::IsComment(){
  5397.     return FALSE;
  5398. }
  5399.  
  5400. XP_Bool CEditLeafElement::IsComment(char* /*prefix*/){
  5401.     return FALSE;
  5402. }
  5403.  
  5404. //
  5405. // Default implementation
  5406. //
  5407. PA_Tag* CEditLeafElement::TagOpen( int /* iEditOffset */ ){
  5408.     PA_Tag *pTag = XP_NEW( PA_Tag );
  5409.     XP_BZERO( pTag, sizeof( PA_Tag ) );
  5410.     if( GetTagData() ){
  5411.         SetTagData( pTag, GetTagData() );
  5412.     }
  5413.     else {
  5414.         SetTagData( pTag, ">" );
  5415.     }
  5416.     return pTag;
  5417. }
  5418.  
  5419. ElementIndex CEditLeafElement::GetPersistentCount()
  5420. {
  5421.     return GetLen();
  5422. }
  5423.  
  5424. void CEditLeafElement::GetAll(CEditSelection& selection) {
  5425.     selection.m_start.m_pElement = this;
  5426.     selection.m_start.m_iPos = 0;
  5427.     selection.m_end.m_pElement = this;
  5428.     selection.m_end.m_iPos = Leaf()->GetLen();
  5429.     selection.m_bFromStart = FALSE;
  5430. }
  5431.  
  5432. XP_Bool CEditLeafElement::IsAcceptableChild(CEditElement& /*pChild*/ ) {return FALSE;}
  5433. XP_Bool CEditLeafElement::IsContainerContainer() { return FALSE; }
  5434.  
  5435. /* Optimized version for leaves. */
  5436.  
  5437. XP_Bool CEditLeafElement::IsFirstInContainer(){
  5438.     CEditContainerElement* pContainer = FindContainer();
  5439.     if ( ! pContainer ) {
  5440.         return TRUE;
  5441.     }
  5442.     CEditElement* pOther = pContainer->GetChild();
  5443.     return pOther == this;
  5444. }
  5445.  
  5446.  
  5447. #ifdef DEBUG
  5448. void CEditLeafElement::ValidateTree(){
  5449.     CEditElement::ValidateTree();
  5450.     CEditElement *pChild = GetChild();
  5451.     XP_ASSERT(!pChild); // Must have no children.
  5452.     // Parent must exist and be a container
  5453.     CEditElement *pParent = GetParent();
  5454.     XP_ASSERT(pParent); 
  5455.     XP_ASSERT(pParent->IsContainer()); 
  5456. }
  5457. #endif
  5458.  
  5459. XP_Bool CEditLeafElement::IsMisspelled() {
  5460.     return (IsText() && Text()->m_tf & TF_SPELL);
  5461. }
  5462.  
  5463.  
  5464. void CEditLeafElement::StreamToPositionalText( IStreamOut *pOut, XP_Bool bEnd ){ 
  5465.     if( !bEnd ){
  5466.         XP_ASSERT( GetLen() == 1 );
  5467.         pOut->Write(" ",1); 
  5468.     }
  5469. }
  5470.  
  5471. //-----------------------------------------------------------------------------
  5472. // CEditContainerElement
  5473. //-----------------------------------------------------------------------------
  5474. //
  5475. // Static constructor to create default element
  5476. //
  5477. CEditContainerElement* CEditContainerElement::NewDefaultContainer( CEditElement *pParent,
  5478.         ED_Alignment align  ){
  5479.     PA_Tag *pTag = XP_NEW( PA_Tag );
  5480.     XP_BZERO( pTag, sizeof( PA_Tag ) );
  5481. #ifdef EDT_DDT
  5482.     pTag->type = P_NSDT;
  5483. #else
  5484.     pTag->type = P_DESC_TITLE;
  5485. #endif
  5486.     // CSID doesn't matter because there's no data in the pTag, so there's no i18n issues.
  5487.     CEditContainerElement *pRet = new CEditContainerElement( pParent, pTag, 0, align );
  5488.     PA_FreeTag( pTag );        
  5489.     return pRet;
  5490. }
  5491.  
  5492. CEditContainerElement::CEditContainerElement(CEditElement *pParent, PA_Tag *pTag, int16 csid,
  5493.                 ED_Alignment defaultAlign ): 
  5494.             CEditElement(pParent, pTag ? pTag->type : P_PARAGRAPH), 
  5495.             m_defaultAlign( defaultAlign ),
  5496.             m_align( defaultAlign ),
  5497.             m_hackPreformat(0),
  5498.             m_bHasEndTag(FALSE)
  5499.  
  5500.     if( pTag ){
  5501.         EDT_ContainerData *pData = ParseParams( pTag, csid );
  5502.         SetData( pData );
  5503.         FreeData( pData );
  5504.     }
  5505. }
  5506.  
  5507. CEditContainerElement::CEditContainerElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer): 
  5508.         CEditElement( pStreamIn, pBuffer )
  5509. {
  5510.     m_defaultAlign = (ED_Alignment) pStreamIn->ReadInt();
  5511.     m_align = (ED_Alignment) pStreamIn->ReadInt();
  5512.     m_bHasEndTag = FALSE;
  5513. }
  5514.  
  5515. void CEditContainerElement::StreamOut( IStreamOut *pOut ){
  5516.     CEditElement::StreamOut( pOut );
  5517.     pOut->WriteInt( m_defaultAlign );
  5518.     pOut->WriteInt( m_align );
  5519. }
  5520.  
  5521. void CEditContainerElement::StreamInChildren(IStreamIn* pIn, CEditBuffer* pBuffer){
  5522.     CEditElement::StreamInChildren(pIn, pBuffer);
  5523.     if ( GetChild() == NULL ){
  5524.         // I need a dummy child.
  5525.         // g++ gets issues a spurious warning about the value not being used.
  5526.         // Creating it automaticly inserts it
  5527.         (void) new CEditTextElement(this, 0);
  5528.     }
  5529. }
  5530.  
  5531. ElementIndex CEditContainerElement::GetPersistentCount(){
  5532.     // One extra element: the end of the last item in the container.
  5533.     return CEditElement::GetPersistentCount() + 1;
  5534. }
  5535.  
  5536.  
  5537.  
  5538. void CEditContainerElement::FinishedLoad( CEditBuffer *pBuffer ){
  5539.     {
  5540.         if ( GetType() != P_PREFORMAT ) {
  5541.             // Don't allow more than one space before non-text elements
  5542.             CEditElement* pNext = 0;
  5543.             for ( CEditElement* pChild = GetChild();
  5544.                 pChild;
  5545.                 pChild = pNext ) {
  5546.                 pNext = pChild->GetNextSibling();
  5547.                 if ( pChild->IsText() && ! (pNext && pNext->IsText()) ){
  5548.                     CEditTextElement* pTextChild = pChild->Text();
  5549.                     char* pText = pTextChild->GetText();
  5550.                     int size = pTextChild->GetLen();
  5551.                     XP_Bool trimming = FALSE;
  5552.                     //do not allow 1 byte of space to be a child
  5553.                     if (size == 1 && GetType() == P_NSDT && pText[0] == ' ') {
  5554.                         trimming = TRUE;
  5555.                         size--;
  5556.                     }
  5557.  
  5558.                     while ( size >0 && pText[size-1] == '\n' || (size > 1 && pText[size-1] == ' ' && pText[size-2] == ' ') ) {
  5559.                         // More than one character of white space at the end of
  5560.                         // a text element will get laid out as one character.
  5561.                         trimming = TRUE;
  5562.                         size--;
  5563.                     }
  5564.                     if ( trimming ) {
  5565.                         if (size <= 0 ) {
  5566.                             delete pChild;
  5567.                         }
  5568.                         else {
  5569.                             char* pCopy = XP_STRDUP(pText);
  5570.                             pCopy[size] = '\0';
  5571.                             pTextChild->SetText(pCopy);
  5572.                             XP_FREE(pCopy);
  5573.                         }
  5574.                     }
  5575.                 }
  5576.             }
  5577.         }
  5578.     }
  5579.     if ( GetChild() == NULL ){
  5580.         // I need a dummy child.
  5581.         CEditTextElement* pChild = new CEditTextElement(this, 0);
  5582.         // Creating it automaticly inserts it
  5583.         pChild->FinishedLoad(pBuffer);
  5584.     }
  5585.  
  5586.  
  5587.     // 
  5588.     // if the last element in a paragraph is a <br>, it is ignored by the browser.  
  5589.     //  So we should trim it.
  5590.     //  we should do this only once!!!
  5591.     CEditElement *pChild;
  5592.     CEditElement* pPrev;
  5593.     if( (pChild = GetLastChild())!= 0 && pChild->IsBreak()) {
  5594.         pPrev = pChild->GetPreviousSibling();
  5595.         if (! (pPrev && pPrev->IsBreak()) )
  5596.             delete pChild;
  5597.     }
  5598.  
  5599.     CEditElement::FinishedLoad(pBuffer);
  5600. }
  5601.  
  5602.  
  5603.  
  5604. XP_Bool CEditContainerElement::ForcesDoubleSpacedNextLine() { 
  5605.     return BitSet( edt_setContainerHasLineAfter, GetType() );
  5606. }
  5607.  
  5608. void CEditContainerElement::AdjustContainers( CEditBuffer *pBuffer ){
  5609.  
  5610.     if( GetType() == P_PARAGRAPH
  5611.         || (pBuffer->IsComposeWindow() 
  5612.                 && GetType() == P_PREFORMAT
  5613.                 && m_hackPreformat ) ){
  5614.         //
  5615.         // Convert to internal paragraph type
  5616.         //
  5617.         SetType( P_NSDT );
  5618.  
  5619.         CEditContainerElement *pPrev = (CEditContainerElement*) GetPreviousSibling();
  5620.         //
  5621.         // Just some paranoia
  5622.         //
  5623.         if ( pPrev && !pPrev->IsContainer() ) pPrev = 0;
  5624.         
  5625.         // 
  5626.         // If there is a previous container, append an empty single spaced 
  5627.         //  paragraph before this one.
  5628.         //
  5629.         if( pPrev && !pPrev->ForcesDoubleSpacedNextLine() ){
  5630.             CEditContainerElement *pNew;
  5631.             pNew = CEditContainerElement::NewDefaultContainer( 0, 
  5632.                         GetAlignment() );
  5633.             // g++ thinks the following value is not used, but it's mistaken.
  5634.             // The constructor auto-inserts the element into the tree.
  5635.             (void) new CEditTextElement(pNew, 0);
  5636.             pNew->InsertBefore( this );
  5637.         }
  5638.     }
  5639.  
  5640.     if( BitSet( edt_setContainerBreakConvert, GetType() )) {
  5641.         // Find any breaks in this paragraph and convert them into paragraphs
  5642.         CEditLeafElement *pChild = (CEditLeafElement*) GetChild();
  5643.         CEditLeafElement *pPrev = 0;
  5644.  
  5645.         while( pChild ){
  5646.             CEditLeafElement *pNext = pChild->LeafInContainerAfter();
  5647.             if(     pChild->IsBreak() 
  5648. #ifdef USE_SCRIPT
  5649.                     && !(pPrev && pPrev->IsText() && 
  5650.                         (pPrev->Text()->m_tf & (TF_SCRIPT|TF_STYLE|TF_SERVER)) != 0
  5651.                         )
  5652.                     && !(pNext && pNext->IsText() && 
  5653.                         (pNext->Text()->m_tf & (TF_SCRIPT|TF_STYLE|TF_SERVER)) != 0
  5654.                         )
  5655. #endif
  5656.                 ){
  5657.                 //                
  5658.                 // If the first thing in the paragraph is a break,
  5659.                 //  make the paragraph contain empty text
  5660.                 //
  5661.                 if( pChild->PreviousLeafInContainer() == 0 ){
  5662.                     CEditTextElement *pNewText = new CEditTextElement((CEditElement*)0, 0);
  5663.                     pNewText->InsertBefore( pChild );
  5664.                 }
  5665.  
  5666.                 // Break the chain of children So unlink doesn't try and
  5667.                 //  merge the chain
  5668.                 pChild->SetNextSibling(0);
  5669.  
  5670.                 // Remove the break.
  5671.                 pChild->Unlink();
  5672.                 delete pChild;
  5673.  
  5674.                 pChild = 0;
  5675.  
  5676.                 // reparent the children.
  5677.                 if( pNext ){
  5678.                     //
  5679.                     // Create a new container
  5680.                     //
  5681.                     CEditContainerElement *pNew;
  5682.                     pNew = CEditContainerElement::NewDefaultContainer( 0, 
  5683.                             GetAlignment() );
  5684.                     pNew->InsertAfter(this);
  5685.  
  5686.                     pNew->SetChild(pNext);
  5687.                     while( pNext ){
  5688.                         pNext->SetParent( pNew );
  5689.                         pNext = (CEditLeafElement*)pNext->GetNextSibling();
  5690.                     }
  5691.                 }
  5692.             }
  5693.             pPrev = pChild;
  5694.             pChild = pNext;
  5695.         }
  5696.     }
  5697.  
  5698.     CEditElement::AdjustContainers(pBuffer);
  5699. }
  5700.  
  5701. PA_Tag* CEditContainerElement::TagOpen( int iEditOffset ){
  5702.     PA_Tag *pRet = 0;
  5703.     PA_Tag* pTag;
  5704.  
  5705.     // create the DIV tag if we need to.
  5706.     if( m_align == ED_ALIGN_ABSCENTER || m_align == ED_ALIGN_RIGHT ){
  5707.         pTag = XP_NEW( PA_Tag );
  5708.         XP_BZERO( pTag, sizeof( PA_Tag ) );
  5709.         if( m_align== ED_ALIGN_RIGHT ){
  5710.             SetTagData( pTag, "ALIGN=right>");
  5711.             pTag->type = P_DIVISION;
  5712.         }
  5713.         else {
  5714.             SetTagData( pTag, ">");
  5715.             pTag->type = P_CENTER;
  5716.         }
  5717.         pRet = pTag;
  5718.     }
  5719.  
  5720.     // create the actual paragraph tag
  5721.     if( GetTagData() ){
  5722.         pTag = XP_NEW( PA_Tag );
  5723.         XP_BZERO( pTag, sizeof( PA_Tag ) );
  5724.         SetTagData( pTag, GetTagData() );
  5725.     }
  5726.     else {
  5727.         pTag = CEditElement::TagOpen( iEditOffset );
  5728.     }
  5729.  
  5730.     // link the tags together.
  5731.     if( pRet == 0 ){
  5732.         pRet = pTag;
  5733.     }
  5734.     else {
  5735.         pRet->next = pTag;
  5736.     }
  5737.     return pRet;
  5738. }
  5739.  
  5740. PA_Tag* CEditContainerElement::TagEnd( ){
  5741.     PA_Tag *pRet = CEditElement::TagEnd();
  5742.     if( m_align == ED_ALIGN_ABSCENTER || m_align == ED_ALIGN_RIGHT ){
  5743.         PA_Tag* pTag = XP_NEW( PA_Tag );
  5744.         XP_BZERO( pTag, sizeof( PA_Tag ) );
  5745.         pTag->is_end = TRUE;
  5746.         if( m_align == ED_ALIGN_RIGHT ){
  5747.             pTag->type = P_DIVISION;
  5748.         }
  5749.         else {
  5750.             pTag->type = P_CENTER;
  5751.         }
  5752.  
  5753.         if( pRet == 0 ){
  5754.             pRet = pTag;
  5755.         }
  5756.         else {
  5757.             pRet->next = pTag;
  5758.         }
  5759.     }
  5760.     return pRet;
  5761. }
  5762.  
  5763. EEditElementType CEditContainerElement::GetElementType()
  5764. {
  5765.     return eContainerElement;
  5766. }
  5767.  
  5768. XP_Bool CEditContainerElement::IsPlainFirstContainer(){
  5769.     if ( (GetType() == P_PARAGRAPH || GetType() == P_NSDT)
  5770.         && ! HasExtraData()
  5771.         && IsFirstContainer() ) {
  5772.         ED_Alignment alignment = GetAlignment();
  5773.         ED_Alignment defaultAlignment = GetDefaultAlignment();
  5774.         if ( alignment == ED_ALIGN_DEFAULT
  5775.             || alignment == defaultAlignment
  5776.             || ( alignment == ED_ALIGN_LEFT
  5777.                 && defaultAlignment == ED_ALIGN_DEFAULT )) {
  5778.             return TRUE;
  5779.         }
  5780.     }
  5781.     return FALSE;
  5782. }
  5783.  
  5784. XP_Bool CEditContainerElement::IsFirstContainer(){
  5785.     CEditSubDocElement* pSubDoc = GetSubDoc();
  5786.     return ! pSubDoc || pSubDoc->GetChild() == this;
  5787. }
  5788.  
  5789. XP_Bool CEditContainerElement::SupportsAlign(){
  5790.     return BitSet( edt_setContainerSupportsAlign, GetType() );
  5791. }
  5792.  
  5793. XP_Bool CEditContainerElement::IsImplicitBreak() {
  5794.     XP_ASSERT( GetType() == P_NSDT );
  5795.     CEditElement *pPrev;
  5796.     return ( (pPrev = GetPreviousSibling()) == 0 
  5797.                 || !pPrev->IsContainer()
  5798.                 || pPrev->Container()->ForcesDoubleSpacedNextLine()
  5799. //                || ((pPrev->Container()->GetAlignment() == ED_ALIGN_RIGHT) && (GetAlignment() != ED_ALIGN_RIGHT) )
  5800. //                || ((pPrev->Container()->GetAlignment() != ED_ALIGN_RIGHT) && (GetAlignment() == ED_ALIGN_RIGHT) )
  5801.                 );
  5802.  
  5803. }
  5804.  
  5805. //helper function
  5806. //default == left!
  5807. PRIVATE XP_Bool CompareAlignments(ED_Alignment x, ED_Alignment y)
  5808. {
  5809.     if (x == y)
  5810.         return TRUE;
  5811.     if ( ((x == ED_ALIGN_ABSCENTER) || (x == ED_ALIGN_CENTER)) 
  5812.         && ((y == ED_ALIGN_ABSCENTER) || (y == ED_ALIGN_CENTER)) )
  5813.             return TRUE;
  5814.     if ( ((x == ED_ALIGN_DEFAULT) || (x == ED_ALIGN_LEFT)) 
  5815.         && ((y == ED_ALIGN_DEFAULT) || (y == ED_ALIGN_LEFT)) )
  5816.             return TRUE;
  5817.     if ( ((x == ED_ALIGN_ABSBOTTOM) || (x == ED_ALIGN_BOTTOM)) 
  5818.         && ((y == ED_ALIGN_ABSBOTTOM) || (y == ED_ALIGN_BOTTOM)) )
  5819.             return TRUE;
  5820.     if ( ((x == ED_ALIGN_ABSTOP) || (x == ED_ALIGN_TOP)) 
  5821.         && ((y == ED_ALIGN_ABSTOP) || (y == ED_ALIGN_TOP)) )
  5822.             return TRUE;
  5823.     return FALSE;
  5824. }
  5825.  
  5826. // 0..2, where 0 = not in pseudo paragraph.
  5827. // 1 = first container of pseudo paragraph.
  5828. // 2 = second container of pseudo paragraph.
  5829. // 3 = after list, <P> does NOT cause a blank line to be drawn above paragraph. we need <P><BR>
  5830. int16 CEditContainerElement::GetPseudoParagraphState(){
  5831.     if( GetType() != P_NSDT ){
  5832.         return 0;
  5833.     }
  5834.     CEditElement *pPrev = GetPreviousSibling();
  5835.     CEditElement *pPrevPrev;
  5836.     if (pPrev)
  5837.         pPrevPrev = pPrev->GetPreviousSibling();
  5838.     else 
  5839.         pPrevPrev=NULL;
  5840.     CEditElement *pNext = GetNextSibling();
  5841.     if ( !pPrev  || (!pPrev->IsContainer()) && !IsEmpty() ) {
  5842.         return 0;
  5843.     }
  5844.     if (!pPrev->IsContainer())
  5845.         return 1;
  5846.     CEditContainerElement* pPrevContainer = pPrev->Container();
  5847.     CEditContainerElement* pPrevPrevContainer=NULL;
  5848.     if ((pPrevPrev) &&(pPrevPrev->IsContainer()))
  5849.         pPrevPrevContainer = pPrevPrev->Container();
  5850.  
  5851.     if ( IsEmpty() && pPrevContainer->GetType() == P_NSDT && pPrevContainer->IsEmpty()
  5852.         && (!pPrevPrevContainer || pPrevPrevContainer->GetType() != P_NSDT) 
  5853.         && pNext && pNext->IsContainer() && pNext->Container()->GetType() == P_NSDT  && pNext->Container()->IsEmpty() ) 
  5854.         {
  5855.         return 5; //special for 3 spaces.  we must settle up the <BR> tags now.  <P> later wont cut it.
  5856.     }
  5857.    
  5858.     if ( IsEmpty() && pPrevContainer->GetType() == P_NSDT && pPrevContainer->IsEmpty()
  5859.         && (!pPrevPrevContainer || pPrevPrevContainer->GetType() != P_NSDT) ) 
  5860.         {
  5861.         return 4;
  5862.     }
  5863.     if ( !IsEmpty() && pPrevContainer->GetType() == P_NSDT && pPrevContainer->IsEmpty()
  5864.         && (!pPrevPrevContainer || pPrevPrevContainer->GetType() != P_NSDT) ) 
  5865.         {
  5866.         return 3;
  5867.     }
  5868.     if ( ! IsEmpty() && pPrevContainer->GetType() == P_NSDT
  5869.          && pPrevContainer->IsEmpty()  ) {
  5870.         return 2;
  5871.     }
  5872.     // Check for state 1
  5873.     if ( IsEmpty() && pNext && pNext->IsContainer() &&
  5874.                 pNext->Container()->GetType() == P_NSDT
  5875.            && ! pNext->Container()->IsEmpty() ) {
  5876.         return 1;
  5877.     }
  5878.     // NOTE: there needs to be a BR tag when the previous non empty container is the same alignment as this one!
  5879.     //the helper function is mainly to make sure default and left are equivalent
  5880.     pPrevContainer= GetPreviousNonEmptyContainer(); //check nonempty!
  5881.     if ( !IsEmpty() && pPrevContainer && !CompareAlignments(pPrevContainer->GetAlignment(),GetAlignment()) )
  5882.         return 1; 
  5883. /*    if ( IsEmpty() && pNext && pNext->IsContainer() 
  5884.            && pNext->Container()->GetType() == P_NSDT
  5885.            && pNext->Container()->IsEmpty() 
  5886.            && pPrevContainer->IsEmpty()
  5887.            ) {
  5888.         return 4;
  5889.     }*/
  5890.     return 0;
  5891. }
  5892.  
  5893.  
  5894.  
  5895. //NOTES:
  5896. /*
  5897. It is not necessary to put a BR tag to break after <CENTER> or <DIV ALIGN=right>.
  5898. there IS an implicit break of sorts at that point
  5899. */
  5900. void CEditContainerElement::PrintOpen( CPrintState *pPrintState ){
  5901.     if ( ShouldSkipTags() ) return;
  5902.  
  5903.     XP_Bool bHasExtraData = HasExtraData();
  5904.  
  5905. #ifdef EDT_DDT
  5906.     if( GetType() == P_NSDT ){
  5907.         int16 ppState = GetPseudoParagraphState();
  5908.         CEditContainerElement* pPrevContainer=GetPreviousNonEmptyContainer();
  5909.         if (( GetAlignment() == ED_ALIGN_RIGHT && ! IsEmpty()) && ( !pPrevContainer || (pPrevContainer->GetAlignment() != ED_ALIGN_RIGHT ) )){
  5910.             pPrintState->m_pOut->Printf( "\n");
  5911.             pPrintState->m_iCharPos = pPrintState->m_pOut->Printf( "<DIV ALIGN=right>"); 
  5912.             
  5913.         }
  5914.         else if (( GetAlignment() == ED_ALIGN_ABSCENTER && !IsEmpty()) && ( !pPrevContainer || (pPrevContainer->GetAlignment() != ED_ALIGN_ABSCENTER ) )){
  5915.             pPrintState->m_pOut->Printf( "\n");
  5916.             pPrintState->m_iCharPos = pPrintState->m_pOut->Printf( "<CENTER>"); 
  5917.         }
  5918.         if( !IsImplicitBreak() && ! bHasExtraData){
  5919.             switch( ppState ){
  5920.             default:
  5921.             case 0:
  5922.                 pPrintState->m_pOut->Printf( "\n");
  5923.                 pPrintState->m_iCharPos = pPrintState->m_pOut->Printf("<BR>"); 
  5924.                 break;
  5925.             case 1:
  5926.                 // Do nothing. This is an empty paragraph.
  5927.                 break;
  5928.             case 2:
  5929.                 pPrintState->m_pOut->Printf( "\n");
  5930.                 pPrintState->m_iCharPos = pPrintState->m_pOut->Printf( "<P>"); 
  5931.                 break;
  5932.             case 3:
  5933.                 pPrintState->m_pOut->Printf( "\n"); //used for 1 space after listitem.
  5934.                 pPrintState->m_iCharPos = pPrintState->m_pOut->Printf( "<P><BR>"); 
  5935.                 break;
  5936.             case 4:
  5937.                 pPrintState->m_pOut->Printf( "\n"); //used for 2 open spaces after listitems for example
  5938.                 pPrintState->m_iCharPos = pPrintState->m_pOut->Printf( "<P> "); //only gives us one.  
  5939.                 //if <P> is comming, we will get 2 line spacing.  if we wanted more, 
  5940.                 //we should have settled up with a 5 on return from pseudo
  5941.                 break;
  5942.             case 5:
  5943.                 pPrintState->m_pOut->Printf( "\n"); //used for 3 open spaces after listitems for example
  5944.                 pPrintState->m_pOut->Printf( "<P> "); //gives 2 spaces after list type item
  5945.                 pPrintState->m_pOut->Printf( "\n"); 
  5946.                 pPrintState->m_iCharPos = pPrintState->m_pOut->Printf( "<BR> ");
  5947.                 break;
  5948.             }
  5949.         }
  5950.         if ( bHasExtraData ) {
  5951.             EDT_ContainerData* pData = GetData();
  5952.             if ( pData && pData->pExtra ) {
  5953.                 pPrintState->m_pOut->Printf( "\n");
  5954.                 if (ppState == 2) pPrintState->m_pOut->Printf( "\n");
  5955.                 char* pTagName = ppState == 2 ? "P" : "DIV";
  5956.                 pPrintState->m_iCharPos = pPrintState->m_pOut->Printf( "<%s %s>", pTagName, pData->pExtra);
  5957.             }
  5958.         }
  5959.         return;
  5960.     }
  5961. #endif
  5962.  
  5963.     pPrintState->m_iCharPos = 0;
  5964.     pPrintState->m_pOut->Printf("\n"); 
  5965.  
  5966.     PA_Tag *pTag = TagOpen(0);
  5967.     while( pTag ){
  5968.         char *pData = 0;
  5969.         if( pTag->data ){
  5970.             PA_LOCK( pData, char*, pTag->data );
  5971.         }
  5972.  
  5973.         if( pData && *pData != '>' ) {
  5974.             pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "<%s %s", 
  5975.                     EDT_TagString(pTag->type), pData );
  5976.         }
  5977.         else {
  5978.             pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "<%s>", 
  5979.                     EDT_TagString(pTag->type));
  5980.         }
  5981.  
  5982.         if( pTag->data ){
  5983.             PA_UNLOCK( pTag->data );
  5984.         }
  5985.  
  5986.         PA_Tag* pDelTag = pTag;
  5987.         pTag = pTag->next;
  5988.         PA_FreeTag(pDelTag);
  5989.  
  5990.         if( GetType() != P_PREFORMAT ){
  5991.             pPrintState->m_pOut->Printf( "\n"); 
  5992.             pPrintState->m_iCharPos = 0;
  5993.         }
  5994.  
  5995.     }
  5996. }
  5997.  
  5998.  
  5999. XP_Bool CEditContainerElement::ShouldSkipTags(){
  6000.     XP_Bool parentIsTableCell = GetParent() && GetParent()->IsTableCell();
  6001.     return parentIsTableCell && IsPlainFirstContainer();
  6002. }
  6003.  
  6004. XP_Bool CEditContainerElement::HasExtraData(){
  6005.     // Does this have data other than the ALIGN tag?
  6006.     XP_Bool hasData = FALSE;
  6007.     EDT_ContainerData* pData = GetData();
  6008.     if ( pData ) {
  6009.         if ( pData->pExtra ) {
  6010.             hasData = TRUE;
  6011.         }
  6012.         FreeData(pData);
  6013.     }
  6014.     return hasData;
  6015. }
  6016.  
  6017. void CEditContainerElement::PrintEnd( CPrintState *pPrintState ){
  6018.     if ( ShouldSkipTags() ) return;
  6019.  
  6020. #ifdef EDT_DDT
  6021.     if( GetType() == P_NSDT ){
  6022.         int16 ppState = GetPseudoParagraphState();
  6023.         XP_Bool bNeedReturn = FALSE;
  6024.         CEditContainerElement* pNextContainer=GetNextNonEmptyContainer();
  6025.         if ( HasExtraData() ){
  6026.             if ( ppState != 2 ){
  6027.                 pPrintState->m_pOut->Printf( "</DIV>");
  6028.                 bNeedReturn = TRUE;
  6029.             }
  6030.         }
  6031.         // Keep empty paragraphs from being eaten
  6032.         if ( IsEmpty() && ppState == 0) {
  6033.             char* space = " ";
  6034.             pPrintState->m_pOut->Printf( space );
  6035.             pPrintState->m_iCharPos += XP_STRLEN(space);
  6036.         }
  6037.         if( (GetAlignment() == ED_ALIGN_RIGHT && !IsEmpty() )&& (!pNextContainer || !CompareAlignments(pNextContainer->GetAlignment(),GetAlignment())) ) {
  6038.             pPrintState->m_pOut->Printf( "</DIV>"); 
  6039.             bNeedReturn = TRUE;
  6040.         }
  6041.         else if(( GetAlignment() == ED_ALIGN_ABSCENTER && !IsEmpty() ) && (!pNextContainer || !CompareAlignments(pNextContainer->GetAlignment(),GetAlignment())) ) {
  6042.             pPrintState->m_pOut->Printf( "</CENTER>"); 
  6043.             bNeedReturn = TRUE;
  6044.         }
  6045.  
  6046.         if ( bNeedReturn ) {
  6047.             pPrintState->m_iCharPos = 0;
  6048.             pPrintState->m_pOut->Printf( "\n");
  6049.         }
  6050.         return;
  6051.     }
  6052. #endif
  6053.  
  6054.  
  6055.     PA_Tag *pTag = TagEnd();
  6056.     while( pTag ){
  6057.         char *pData = 0;
  6058.         if( pTag->data ){
  6059.             PA_LOCK( pData, char*, pTag->data );
  6060.         }
  6061.  
  6062.         pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "</%s>",
  6063.             EDT_TagString( pTag->type ) );
  6064.  
  6065.         if( pTag->data ){
  6066.             PA_UNLOCK( pTag->data );
  6067.         }
  6068.  
  6069.         PA_Tag* pDelTag = pTag;
  6070.         pTag = pTag->next;
  6071.         PA_FreeTag(pDelTag);
  6072.     }
  6073.  
  6074.     pPrintState->m_iCharPos = 0;
  6075.     pPrintState->m_pOut->Printf( "\n"); 
  6076. }
  6077.  
  6078.  
  6079.  
  6080. CEditElement* CEditContainerElement::Clone( CEditElement* pParent ){
  6081.     PA_Tag *pTag = TagOpen(0);
  6082.  
  6083.     // LTNOTE: we get division tags at the beginning of paragraphs
  6084.     //  for their alignment. Ignore these tags, we get it directly from the
  6085.     //  container itself.
  6086.     //  Its a total hack.
  6087.     //
  6088.     if( pTag->type == P_DIVISION || pTag->type == P_CENTER ){
  6089.         PA_Tag *pDelTag = pTag;
  6090.         pTag = pTag->next;
  6091.         PA_FreeTag( pDelTag );
  6092.     }
  6093.     CEditElement *pRet = new CEditContainerElement(pParent, pTag, GetWinCSID(), m_align );
  6094.     PA_FreeTag( pTag );
  6095.     return pRet;
  6096. }
  6097.  
  6098. // Property Getting and Setting Stuff.
  6099.  
  6100. EDT_ContainerData* CEditContainerElement::NewData(){
  6101.     EDT_ContainerData *pData = XP_NEW( EDT_ContainerData );
  6102.     pData->align = ED_ALIGN_LEFT;
  6103.     pData->pExtra = 0;
  6104.     return pData;
  6105. }
  6106.  
  6107. void CEditContainerElement::FreeData( EDT_ContainerData *pData ){
  6108.     if ( pData ) {
  6109.         XP_FREEIF(pData->pExtra);
  6110.         XP_FREE( pData );
  6111.     }
  6112. }
  6113.  
  6114. void CEditContainerElement::SetData( EDT_ContainerData *pData ){
  6115.     char *pNew = 0;
  6116.  
  6117.     m_align = pData->align;
  6118.     if( m_align == ED_ALIGN_CENTER ) m_align = ED_ALIGN_ABSCENTER;
  6119.  
  6120.     //
  6121.     // Never generate ALIGN= stuff anymore.  Use the Div tag and Center Tags
  6122.     //
  6123.     char* pExtra = "";
  6124.     if ( pData && pData->pExtra ) {
  6125.         pExtra = pData->pExtra;
  6126.     }
  6127.     pNew = PR_sprintf_append( pNew, "%s>", pExtra );
  6128.  
  6129.     SetTagData( pNew );
  6130.     free(pNew);
  6131. }
  6132.  
  6133. EDT_ContainerData* CEditContainerElement::GetData( ){
  6134.     EDT_ContainerData *pRet;
  6135.     PA_Tag* pTag = TagOpen(0);
  6136.     pRet = ParseParams( pTag, GetWinCSID() );
  6137.     EDT_DeleteTagChain( pTag );
  6138.     return pRet;
  6139. }
  6140.  
  6141. static char* containerParams[] = {
  6142.     PARAM_ALIGN,
  6143.     "nscisaw",
  6144.     0
  6145. };
  6146.  
  6147. EDT_ContainerData* CEditContainerElement::ParseParams( PA_Tag *pTag, int16 csid ){
  6148.     EDT_ContainerData *pData = NewData();
  6149.     ED_Alignment align;
  6150.  
  6151.     m_hackPreformat = edt_FetchParamBoolExist(pTag, "nscisaw", csid);
  6152.     
  6153.     align = edt_FetchParamAlignment( pTag, m_defaultAlign, FALSE, csid );
  6154.     if( align == ED_ALIGN_CENTER ) align =  ED_ALIGN_ABSCENTER;
  6155.  
  6156.     if( align == ED_ALIGN_RIGHT || align == ED_ALIGN_LEFT  || align == ED_ALIGN_ABSCENTER){
  6157.         pData->align = align;
  6158.     }
  6159.     pData->pExtra = edt_FetchParamExtras( pTag, containerParams, csid );
  6160.     return pData;
  6161. }
  6162.  
  6163. void CEditContainerElement::SetAlignment( ED_Alignment eAlign ){
  6164.     EDT_ContainerData *pData = GetData();
  6165.     pData->align = eAlign;
  6166.     SetData( pData );
  6167.     FreeData( pData );
  6168. }
  6169.  
  6170. void CEditContainerElement::CopyAttributes(CEditContainerElement *pFrom ){
  6171.     SetType( pFrom->GetType() );
  6172.     SetAlignment( pFrom->GetAlignment() );
  6173. }
  6174.  
  6175. XP_Bool CEditContainerElement::IsEmpty(){
  6176.     CEditElement *pChild = GetChild();
  6177.     return ( pChild == 0 
  6178.                 || ( pChild->IsA( P_TEXT )
  6179.                     && pChild->Text()->GetLen() == 0
  6180.                     && pChild->LeafInContainerAfter() == 0 )
  6181.             );
  6182. }
  6183.  
  6184. #ifdef DEBUG
  6185. void CEditContainerElement::ValidateTree(){
  6186.     CEditElement::ValidateTree();
  6187.     CEditElement* pChild = GetChild();
  6188.     XP_ASSERT(pChild); // Must have at least one child.
  6189. }
  6190. #endif
  6191.  
  6192. // 
  6193. // Sets the alignment if the container is empty (or almost empty).  Used only
  6194. //  during the parse phase to handle things like <p><center><img>
  6195. //
  6196. void CEditContainerElement::AlignIfEmpty( ED_Alignment eAlign ){
  6197.     CEditElement *pChild = GetChild();
  6198.     while( pChild ){
  6199.         if( pChild->IsA( P_TEXT ) 
  6200.                 && pChild->Leaf()->GetLen() != 0 ){
  6201.             return;
  6202.         }
  6203.         pChild = pChild->GetNextSibling();
  6204.     }
  6205.     SetAlignment( eAlign );
  6206. }
  6207.  
  6208. void CEditContainerElement::StreamToPositionalText( IStreamOut *pOut, XP_Bool bEnd ){ 
  6209.     if( bEnd ){
  6210.         pOut->Write(" ",1); 
  6211.     }
  6212. }
  6213.  
  6214. char* CEditContainerElement::GetText()
  6215. {
  6216.     char *pText = NULL;
  6217.     CEditElement *pChild = GetChild();
  6218.     while( pChild )
  6219.     {
  6220.         if( pChild->IsText() && pChild->Leaf()->GetLen() != 0 )
  6221.         {
  6222.             pText = PR_sprintf_append( pText, pChild->Text()->GetText() );
  6223.         }
  6224.         pChild = pChild->GetNextSibling();
  6225.     }
  6226.     return pText;
  6227. }
  6228.  
  6229.  
  6230. //-----------------------------------------------------------------------------
  6231. // CEditListElement
  6232. //-----------------------------------------------------------------------------
  6233.  
  6234. CEditListElement::CEditListElement( 
  6235.         CEditElement *pParent, PA_Tag *pTag, int16 /*csid*/ ):
  6236.             CEditElement( pParent, pTag->type ){
  6237.    
  6238.     if( pTag ){
  6239.         char *locked_buff;
  6240.                 
  6241.         PA_LOCK(locked_buff, char *, pTag->data );
  6242.         SetTagData( locked_buff );
  6243.         PA_UNLOCK(pTag->data);
  6244.     }
  6245.     m_pBaseURL = NULL;
  6246. }
  6247.  
  6248. CEditListElement::CEditListElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer): 
  6249.         CEditElement( pStreamIn, pBuffer )
  6250. {
  6251.     m_pBaseURL = NULL;  
  6252. }
  6253.  
  6254. CEditListElement::~CEditListElement()
  6255. {
  6256.   XP_FREEIF(m_pBaseURL);
  6257. }
  6258.  
  6259. PA_Tag* CEditListElement::TagOpen( int /* iEditOffset */ ){
  6260.     PA_Tag *pTag = XP_NEW( PA_Tag );
  6261.     XP_BZERO( pTag, sizeof( PA_Tag ) );
  6262.     SetTagData( pTag, GetTagData() ? GetTagData() : ">" );
  6263.     return pTag;
  6264. }
  6265.  
  6266. CEditElement* CEditListElement::Clone( CEditElement *pParent ){ 
  6267.     PA_Tag* pTag = TagOpen(0);
  6268.     CEditElement* pNew = new CEditListElement( pParent, pTag, GetWinCSID() );
  6269.     PA_FreeTag( pTag );
  6270.     return pNew;
  6271. }
  6272.  
  6273. // Property Getting and Setting Stuff.
  6274.  
  6275. EDT_ListData* CEditListElement::NewData(){
  6276.     EDT_ListData *pData = XP_NEW( EDT_ListData );
  6277.     if( pData == 0 ){
  6278.         // throw();
  6279.         return pData;
  6280.     }
  6281.     pData->iTagType = P_UNUM_LIST;
  6282.     pData->bCompact = FALSE;
  6283.     pData->eType = ED_LIST_TYPE_DEFAULT;
  6284.     pData->iStart = 1;
  6285.     pData->pExtra = 0;
  6286.     pData->pBaseURL = NULL;
  6287.     return pData;
  6288. }
  6289.  
  6290. void CEditListElement::FreeData( EDT_ListData *pData ){
  6291.     if( pData->pExtra ) XP_FREE( pData->pExtra );
  6292.     XP_FREEIF(pData->pBaseURL);
  6293.     XP_FREE( pData );
  6294. }
  6295.  
  6296. void CEditListElement::SetData( EDT_ListData *pData ){
  6297.     char *pNew = 0;
  6298.  
  6299.     // Copy in pBaseURL.
  6300.     XP_FREEIF(m_pBaseURL);
  6301.     if (pData->pBaseURL && *pData->pBaseURL) {
  6302.       m_pBaseURL = XP_STRDUP(pData->pBaseURL);
  6303.     }    
  6304.  
  6305.     SetType( pData->iTagType );
  6306.     
  6307.     if( pData->bCompact ){
  6308.         pNew = PR_sprintf_append( pNew, " COMPACT");
  6309.     }
  6310.     if( pData->iStart != 1 ){
  6311.         pNew = PR_sprintf_append( pNew, " START=%d", pData->iStart );
  6312.     }
  6313.     switch( pData->eType ){
  6314.         case ED_LIST_TYPE_DEFAULT:
  6315.             break;
  6316.         case ED_LIST_TYPE_DIGIT:
  6317.             SetType( P_NUM_LIST );
  6318.             break;
  6319.         case ED_LIST_TYPE_BIG_ROMAN:
  6320.             SetType( P_NUM_LIST );
  6321.             pNew = PR_sprintf_append( pNew, " TYPE=I" );
  6322.             break;
  6323.         case ED_LIST_TYPE_SMALL_ROMAN:
  6324.             SetType( P_NUM_LIST );
  6325.             pNew = PR_sprintf_append( pNew, " TYPE=i" );
  6326.             break;
  6327.         case ED_LIST_TYPE_BIG_LETTERS:
  6328.             SetType( P_NUM_LIST );
  6329.             pNew = PR_sprintf_append( pNew, " TYPE=A" );
  6330.             break;
  6331.         case ED_LIST_TYPE_SMALL_LETTERS:
  6332.             SetType( P_NUM_LIST );
  6333.             pNew = PR_sprintf_append( pNew, " TYPE=a" );
  6334.             break;
  6335.         case ED_LIST_TYPE_CIRCLE:
  6336.             SetType( P_UNUM_LIST );
  6337.             pNew = PR_sprintf_append( pNew, " TYPE=CIRCLE" );
  6338.             break;
  6339.         case ED_LIST_TYPE_SQUARE:
  6340.             SetType( P_UNUM_LIST );
  6341.             pNew = PR_sprintf_append( pNew, " TYPE=SQUARE" );
  6342.             break;
  6343.         case ED_LIST_TYPE_DISC:
  6344.             SetType( P_UNUM_LIST );
  6345.             pNew = PR_sprintf_append( pNew, " TYPE=DISC" );
  6346.             break;
  6347.         case ED_LIST_TYPE_CITE:
  6348.             SetType( P_BLOCKQUOTE );
  6349.             pNew = PR_sprintf_append( pNew, " TYPE=CITE" );
  6350.             break;
  6351.     }
  6352.  
  6353.     if( pData->pExtra  ){
  6354.         pNew = PR_sprintf_append( pNew, " %s", pData->pExtra );
  6355.     }
  6356.  
  6357.     if( pNew ){
  6358.         pNew = PR_sprintf_append( pNew, ">" );
  6359.     }
  6360.  
  6361.     SetTagData( pNew );
  6362.  
  6363.     XP_FREEIF(pNew);
  6364. }
  6365.  
  6366. EDT_ListData* CEditListElement::GetData( ){
  6367.     EDT_ListData *pRet;
  6368.     PA_Tag* pTag = TagOpen(0);
  6369.     pRet = ParseParams( pTag, GetWinCSID() );
  6370.     PA_FreeTag( pTag );
  6371.  
  6372.     if (pRet && m_pBaseURL) {
  6373.       pRet->pBaseURL = XP_STRDUP(m_pBaseURL);
  6374.     }
  6375.     return pRet;
  6376. }
  6377.  
  6378. static char* listParams[] = {
  6379.     PARAM_TYPE,
  6380.     PARAM_COMPACT,
  6381.     PARAM_START,
  6382.     0
  6383. };
  6384.  
  6385. EDT_ListData* CEditListElement::ParseParams( PA_Tag *pTag, int16 csid ){
  6386.     EDT_ListData *pData = NewData();
  6387.     char *pType;
  6388.  
  6389.     pData->iTagType = pTag->type;
  6390.     pData->bCompact = edt_FetchParamBoolExist( pTag, PARAM_COMPACT, csid );
  6391.  
  6392.     pType = edt_FetchParamString( pTag, PARAM_TYPE, csid );
  6393.     if( pType == 0 ){
  6394.         if( pTag->type == P_NUM_LIST ){
  6395.             pData->eType = ED_LIST_TYPE_DIGIT;
  6396.         }
  6397.         else {
  6398.             pData->eType = ED_LIST_TYPE_DEFAULT;
  6399.         }
  6400.     }
  6401.     else {
  6402.         switch (*pType){
  6403.         case 'A':
  6404.             pData->eType = ED_LIST_TYPE_BIG_LETTERS;
  6405.             break;
  6406.         case 'a':
  6407.             pData->eType = ED_LIST_TYPE_SMALL_LETTERS;
  6408.             break;
  6409.         case 'i':
  6410.             pData->eType = ED_LIST_TYPE_SMALL_ROMAN;
  6411.             break;
  6412.         case 'I':
  6413.             pData->eType = ED_LIST_TYPE_BIG_ROMAN;
  6414.             break;
  6415.         case 'c':
  6416.         case 'C':
  6417.             if ( XP_STRNCASECMP(pType, "cite", 4) == 0) {
  6418.                 pData->eType = ED_LIST_TYPE_CITE;
  6419.             }
  6420.             else {
  6421.                 pData->eType = ED_LIST_TYPE_CIRCLE;
  6422.             }
  6423.             break;
  6424.         case 'd':
  6425.         case 'D':
  6426.             pData->eType = ED_LIST_TYPE_DISC;
  6427.             break;
  6428.         case 's':
  6429.         case 'S':
  6430.             pData->eType = ED_LIST_TYPE_SQUARE;
  6431.             break;
  6432.         default:
  6433.             // To do: Figure out how to preserve unknown list types.
  6434.             pData->eType = ED_LIST_TYPE_DEFAULT;
  6435.             break;
  6436.         }
  6437.     }
  6438.  
  6439.     pData->iStart = edt_FetchParamInt( pTag, PARAM_START, 1, csid );
  6440.     pData->pExtra = edt_FetchParamExtras( pTag, listParams, csid );
  6441.     return pData;
  6442. }
  6443.  
  6444. void CEditListElement::CopyData(CEditListElement* pOther){
  6445.     EDT_ListData* pData = pOther->GetData();
  6446.     SetData(pData);
  6447.     FreeData(pData);
  6448. }
  6449.  
  6450. XP_Bool CEditListElement::IsMailQuote(){
  6451.     XP_Bool bResult = FALSE;
  6452.     EDT_ListData* pData = GetData();
  6453.     if ( pData->iTagType == P_BLOCKQUOTE &&
  6454.         pData->eType == ED_LIST_TYPE_CITE ) {
  6455.         bResult = TRUE;
  6456.     }
  6457.     FreeData(pData);
  6458.     return bResult;
  6459. }
  6460.  
  6461. #ifdef DEBUG
  6462. void CEditListElement::ValidateTree(){
  6463.     CEditElement::ValidateTree();
  6464.     // Must have at least one child.
  6465.     XP_ASSERT(GetChild());
  6466.     // We rely on the children to check that
  6467.     // they can have a list as a parent.
  6468. }
  6469. #endif
  6470.  
  6471. EEditElementType CEditListElement::GetElementType(){
  6472.     return eListElement;
  6473. }
  6474.  
  6475. //-----------------------------------------------------------------------------
  6476. // CEditTextElement
  6477. //-----------------------------------------------------------------------------
  6478.  
  6479. CEditTextElement::CEditTextElement(CEditElement *pParent, char *pText):
  6480.         CEditLeafElement(pParent, P_TEXT), 
  6481.         m_pFirstLayoutElement(0),
  6482.         m_pTextBlock(0),
  6483.         m_tf(TF_NONE),
  6484.         m_iFontSize(DEF_FONTSIZE),
  6485.         m_href(ED_LINK_ID_NONE),
  6486.         m_color(ED_Color::GetUndefined()),
  6487.         m_pFace(NULL),
  6488.         m_iWeight(ED_FONT_WEIGHT_NORMAL),
  6489.         m_iPointSize(ED_FONT_POINT_SIZE_DEFAULT),
  6490.         m_pScriptExtra(NULL)
  6491.  
  6492. {
  6493.     if( pText && *pText ){
  6494.         m_pText = XP_STRDUP(pText);             // should be new??
  6495.         m_textSize = XP_STRLEN(m_pText)+1;
  6496.     }
  6497.     else {
  6498.         m_pText = 0;
  6499.         m_textSize = 0;
  6500.     }
  6501. }
  6502.  
  6503. CEditTextElement::CEditTextElement( IStreamIn *pIn, CEditBuffer* pBuffer ):
  6504.         CEditLeafElement(pIn, pBuffer), 
  6505.         m_pFirstLayoutElement(0),
  6506.         m_pTextBlock(0),
  6507.         m_tf(TF_NONE),
  6508.         m_iFontSize(DEF_FONTSIZE),
  6509.         m_href(ED_LINK_ID_NONE),
  6510.         m_pText(0),
  6511.         m_color(ED_Color::GetUndefined()),
  6512.         m_pFace(NULL),
  6513.         m_pScriptExtra(NULL)
  6514. {
  6515.     m_tf = (ED_TextFormat)pIn->ReadInt( );
  6516.     
  6517.     m_iFontSize = pIn->ReadInt( );
  6518.     
  6519.     m_color = pIn->ReadInt( );
  6520.     
  6521.     if( m_tf & TF_HREF ){
  6522.         char* pHref = pIn->ReadZString();
  6523.         char* pExtra = pIn->ReadZString();
  6524.         SetHREF( pBuffer->linkManager.Add( pHref, pExtra ));
  6525.         XP_FREE( pHref );
  6526.         XP_FREE( pExtra );
  6527.     }
  6528.  
  6529.     if ( m_tf & TF_FONT_FACE ) {
  6530.         m_pFace = pIn->ReadZString();
  6531.     }
  6532.  
  6533.     if ( m_tf & TF_FONT_WEIGHT ) {
  6534.         m_iWeight = (int16) pIn->ReadInt();
  6535.     }
  6536.  
  6537.     if ( m_tf & TF_FONT_POINT_SIZE ) {
  6538.         m_iPointSize = (int16) pIn->ReadInt();
  6539.     }
  6540.  
  6541.     if ( EDT_IS_SCRIPT(m_tf) ) {
  6542.         m_pScriptExtra = pIn->ReadZString();
  6543.     }
  6544.  
  6545.     m_pText = pIn->ReadZString();
  6546.     if( m_pText ){
  6547.         m_textSize = XP_STRLEN( m_pText );
  6548.     }
  6549.     else {
  6550.         m_textSize = 0;
  6551.     }
  6552. }
  6553.  
  6554. CEditTextElement::~CEditTextElement(){
  6555.     DisconnectLayoutElements((LO_Element*) m_pFirstLayoutElement);
  6556.     if ( m_pText) XP_FREE(m_pText); 
  6557.     if( m_href != ED_LINK_ID_NONE ) m_href->Release();
  6558.     if ( m_pFace ) {
  6559.         XP_FREE(m_pFace);
  6560.         m_pFace = NULL;
  6561.     }
  6562.     if ( m_pScriptExtra ) {
  6563.         XP_FREE(m_pScriptExtra);
  6564.         m_pScriptExtra = NULL;
  6565.     }
  6566. }
  6567.  
  6568. XP_Bool CEditTextElement::GetLOElementAndOffset( ElementOffset iEditOffset, XP_Bool bEditStickyAfter,
  6569.             LO_Element*& pRetText,
  6570.             int& pLayoutOffset ){
  6571.     return GetLOTextAndOffset( iEditOffset, bEditStickyAfter, *(LO_TextStruct**)&pRetText, pLayoutOffset);
  6572. }
  6573.  
  6574.  
  6575. void CEditTextElement::StreamOut( IStreamOut *pOut ){
  6576.     CEditSelection all;
  6577.     GetAll(all);
  6578.     PartialStreamOut(pOut, all );
  6579. }
  6580.  
  6581. void CEditTextElement::PartialStreamOut( IStreamOut *pOut, CEditSelection& selection ){
  6582.     CEditSelection local;
  6583.     if ( ClipToMe(selection, local) ) {
  6584.  
  6585.         // Fake a stream out.
  6586.         CEditLeafElement::StreamOut( pOut  );
  6587.     
  6588.         pOut->WriteInt( m_tf );
  6589.     
  6590.         pOut->WriteInt( m_iFontSize );
  6591.     
  6592.         pOut->WriteInt( m_color.GetAsLong() );
  6593.     
  6594.         if( m_tf & TF_HREF ){
  6595.             pOut->WriteZString( m_href->hrefStr );
  6596.             pOut->WriteZString( m_href->pExtra );
  6597.         }
  6598.  
  6599.         if( m_tf & TF_FONT_FACE ){
  6600.             pOut->WriteZString( m_pFace );
  6601.         }
  6602.         if( m_tf & TF_FONT_WEIGHT ){
  6603.             pOut->WriteInt(m_iWeight);
  6604.         }
  6605.         if( m_tf & TF_FONT_POINT_SIZE ){
  6606.            pOut->WriteInt(m_iPointSize);
  6607.         }
  6608.  
  6609.         if( EDT_IS_SCRIPT(m_tf) ){
  6610.             pOut->WriteZString( m_pScriptExtra );
  6611.         }
  6612.  
  6613.         pOut->WritePartialZString( m_pText, local.m_start.m_iPos, local.m_end.m_iPos);
  6614.  
  6615.         // No children
  6616.         pOut->WriteInt((int32)eElementNone);
  6617.     }
  6618. }
  6619.  
  6620.  
  6621. EEditElementType CEditTextElement::GetElementType()
  6622. {
  6623.     return eTextElement;
  6624. }
  6625.  
  6626. void CEditTextElement::SetLayoutElement( intn iEditOffset, intn lo_type, 
  6627.                         LO_Element* pLoElement ){
  6628.     
  6629.     /* this element might be our text block. if so, we want to keep track of it */
  6630.     if ( lo_type == LO_TEXTBLOCK ){
  6631. #if defined( DEBUG_shannon )
  6632.         XP_TRACE(("Setting text block to %lx", pLoElement));
  6633. #endif
  6634.         XP_ASSERT ( pLoElement != NULL );
  6635.         m_pTextBlock = &pLoElement->lo_textBlock;
  6636.         pLoElement->lo_any.edit_element = this;
  6637.         pLoElement->lo_any.edit_offset = iEditOffset;
  6638.     }
  6639.     
  6640.     SetLayoutElementHelper(LO_TEXT, (LO_Element**) &m_pFirstLayoutElement,
  6641.         iEditOffset, lo_type, pLoElement);
  6642. }
  6643.  
  6644. LO_Element* CEditTextElement::GetLayoutElement(){ 
  6645.     return m_pFirstLayoutElement; 
  6646. }
  6647.  
  6648. void CEditTextElement::ResetLayoutElement( intn iEditOffset, 
  6649.             LO_Element* pLoElement ){
  6650.     ResetLayoutElementHelper((LO_Element**) &m_pFirstLayoutElement,
  6651.         iEditOffset, pLoElement);
  6652. }
  6653.  
  6654. void edt_RemoveNBSP( int16 csid, char *pString ){
  6655.     while( pString && *pString ){
  6656.         if( *pString == NON_BREAKING_SPACE ){
  6657.             *pString = ' ';
  6658.         }
  6659.         pString = INTL_NextChar(csid, pString);
  6660.     }
  6661. }
  6662.  
  6663. void CEditTextElement::SetText( char *pText, XP_Bool bConvertSpaces, int16 csid){
  6664.     if( m_pText ){
  6665.         XP_FREE( m_pText );
  6666.     }
  6667.     if( pText && *pText ){
  6668.         m_pText = XP_STRDUP(pText);             // should be new??
  6669.         m_textSize = XP_STRLEN(m_pText)+1;
  6670.         if( bConvertSpaces ){
  6671.             edt_RemoveNBSP( csid, m_pText );
  6672.         }
  6673.     }
  6674.     else {
  6675.         m_pText = 0;
  6676.         m_textSize = 0;
  6677.     }
  6678. }
  6679.  
  6680.  
  6681. void CEditTextElement::SetColor( ED_Color iColor ){ 
  6682.     m_color = iColor; 
  6683.     // Dont set color if we are a script
  6684.     if(!iColor.IsDefined() || EDT_IS_SCRIPT(m_tf) ){
  6685.         m_tf  &= ~TF_FONT_COLOR;
  6686.     }
  6687.     else {
  6688.         m_tf |= TF_FONT_COLOR;
  6689.     }
  6690. }
  6691.  
  6692. void CEditTextElement::SetFontSize( int iSize ){
  6693.     m_iFontSize = iSize; 
  6694.     if( m_iFontSize > 7 ) m_iFontSize = 7;
  6695.     // if( m_iFontSize < 1 ) m_iFontSize = 1;
  6696.     
  6697.     //Override (use default for) ALL font size requests for Java Script
  6698.     if( EDT_IS_SCRIPT(m_tf) ){
  6699.         iSize = 0;
  6700.     }
  6701.     
  6702.     // Use 0 (or less) to signify changing to default
  6703.     if( iSize <= 0 ){
  6704.         m_iFontSize = GetDefaultFontSize();
  6705.     }
  6706.     if( m_iFontSize == GetDefaultFontSize() ){
  6707.         m_tf  &= ~TF_FONT_SIZE;
  6708.     }
  6709.     else {
  6710.         m_tf |= TF_FONT_SIZE;
  6711.     }
  6712.     // If using relative font size, then absolute point size must be default
  6713.     // Note that we do this even if setting to default size,
  6714.     //  so this overrides any existing point size
  6715.     m_tf &= ~TF_FONT_POINT_SIZE;
  6716.     m_iPointSize = ED_FONT_POINT_SIZE_DEFAULT;
  6717. }
  6718.  
  6719. void CEditTextElement::SetFontFace( char* pFace ){
  6720.     if ( m_pFace ) {
  6721.         XP_FREE(m_pFace);
  6722.         m_pFace = NULL;
  6723.         m_tf &= ~TF_FONT_FACE;
  6724.     }
  6725.     if ( pFace ) {
  6726.         m_tf |= TF_FONT_FACE;
  6727.         // Skip over initial separator signal
  6728.         if( *pFace == '_' ){
  6729.             pFace++;
  6730.         }
  6731.         m_pFace = XP_STRDUP(pFace);
  6732.     }
  6733. }
  6734.  
  6735. void CEditTextElement::SetFontWeight( int16 weight ){
  6736.     if ( m_iWeight != ED_FONT_WEIGHT_NORMAL ) {
  6737.         m_tf &= ~TF_FONT_WEIGHT;
  6738.         m_iWeight = ED_FONT_WEIGHT_NORMAL;
  6739.     }
  6740.     if ( weight != ED_FONT_WEIGHT_NORMAL ) {
  6741.         m_tf |= TF_FONT_WEIGHT;
  6742.         m_iWeight = weight;
  6743.     }
  6744. }
  6745.  
  6746. void CEditTextElement::SetFontPointSize( int16 pointSize ){
  6747.     if ( m_iPointSize != ED_FONT_POINT_SIZE_DEFAULT ) {
  6748.         m_tf &= ~TF_FONT_POINT_SIZE;
  6749.         m_iPointSize = ED_FONT_POINT_SIZE_DEFAULT;
  6750.     }
  6751.     if ( pointSize != ED_FONT_POINT_SIZE_DEFAULT ) {
  6752.         m_tf |= TF_FONT_POINT_SIZE;
  6753.         m_iPointSize = pointSize;
  6754.         // If using absolute point size, then relative font size must be default
  6755.         m_iFontSize = GetDefaultFontSize();
  6756.         m_tf  &= ~TF_FONT_SIZE;
  6757.     }
  6758. }
  6759.  
  6760. void CEditTextElement::SetScriptExtra( char* pScriptExtra ){
  6761.     if ( m_pScriptExtra ) {
  6762.         XP_FREE(m_pScriptExtra);
  6763.         m_pScriptExtra = NULL;
  6764.     }
  6765.     if ( pScriptExtra ) {
  6766.         m_pScriptExtra = XP_STRDUP(pScriptExtra);
  6767.     }
  6768. }
  6769.  
  6770. void CEditTextElement::SetHREF( ED_LinkId id ){
  6771.     if( m_href != ED_LINK_ID_NONE ){
  6772.         m_href->Release();
  6773.     }
  6774.     m_href = id;
  6775.     // Dont set HREF if we are a script
  6776.     if( id != ED_LINK_ID_NONE && !EDT_IS_SCRIPT(m_tf) ){
  6777.         id->AddRef();
  6778.         m_tf |= TF_HREF;
  6779.     }
  6780.     else {
  6781.         m_tf &= ~TF_HREF;
  6782.     }
  6783. }
  6784.  
  6785. int CEditTextElement::GetFontSize(){ 
  6786.     if( m_tf & TF_FONT_SIZE ){
  6787.         return m_iFontSize;
  6788.     }
  6789.     else {
  6790.         return GetDefaultFontSize();
  6791.     }
  6792. }
  6793.  
  6794. char* CEditTextElement::GetFontFace(){ 
  6795.     return m_pFace;
  6796. }
  6797.  
  6798. int16 CEditTextElement::GetFontWeight(){ 
  6799.     return m_iWeight;
  6800. }
  6801.  
  6802. int16 CEditTextElement::GetFontPointSize(){ 
  6803.     return m_iPointSize;
  6804. }
  6805.  
  6806. char* CEditTextElement::GetScriptExtra(){ 
  6807.     return m_pScriptExtra;
  6808. }
  6809.  
  6810. // 
  6811. // The the current text buffer but convert spaces to non breaking spaces where
  6812. //  necessary so the file can be laid out properly. 
  6813. //
  6814. //  The rules for converting spaces into Non breaking spaces are as follows:
  6815. //
  6816. //  X = character
  6817. //  N = Non breaking space
  6818. //  S = Space
  6819. //  ^ = End of line
  6820. //
  6821. //                      1           2           N
  6822. //                  ---------------------------------------
  6823. //  Line Start          N           NS          NNNS
  6824. //  Middle              XS          XNS         XNNNS
  6825. //  End                 N^          XNN^        XNNN^
  6826. //
  6827. //
  6828. char* CEditTextElement::GetTextWithConvertedSpaces(){
  6829.  
  6830.     // If regular get text doesn't do anything, neither should we.
  6831.     if( GetText() == 0 ){
  6832.         return 0;
  6833.     }
  6834.  
  6835.     // grab a working buffer to copy the data into
  6836.     char *pBuf = edt_WorkBuf( GetLen()+1 );
  6837.  
  6838.     // Out of memory
  6839.     if( pBuf == 0 ) return 0;
  6840.  
  6841.     CEditElement *pPrev = PreviousLeafInContainer();
  6842.     CEditElement *pNext = LeafInContainerAfter();
  6843.  
  6844.     XP_Bool bNextIsSpace, bNextIsEOL, bIsSpace, bAtBeginOfLine;
  6845.     
  6846.  
  6847.     // if we are the beginning of a line
  6848.     bAtBeginOfLine = ( pPrev == 0 || pPrev->CausesBreakAfter() );
  6849.  
  6850.     char *pDest = pBuf;
  6851.     const char *pSrc = GetText();
  6852.  
  6853.     while( *pSrc ) {
  6854.  
  6855.         bIsSpace = (*pSrc == ' ');
  6856.  
  6857.         if( pSrc[1] != 0 ){
  6858.             bNextIsEOL = FALSE;
  6859.             bNextIsSpace = (pSrc[1] == ' ');
  6860.         }
  6861.         else if( pNext
  6862.                 && pNext->IsText()
  6863.                 && pNext->Text()->GetText()) {
  6864.             bNextIsEOL = FALSE;
  6865.             bNextIsSpace = ( pNext->Text()->GetText()[0] == ' ' );
  6866.         }
  6867.         else {
  6868.             bNextIsEOL = TRUE;
  6869.             bNextIsSpace = FALSE;
  6870.         }
  6871.  
  6872.         if( bAtBeginOfLine ){
  6873.             if( bIsSpace ){
  6874.                 *pDest = (char) NON_BREAKING_SPACE;
  6875.             }
  6876.             else {
  6877.                 *pDest = *pSrc;
  6878.             }
  6879.             bAtBeginOfLine = FALSE;
  6880.         }
  6881.         else {
  6882.             if( bIsSpace && ( bNextIsSpace || bNextIsEOL )){
  6883.                 *pDest = (char) NON_BREAKING_SPACE;
  6884.             }
  6885.             else {
  6886.                 *pDest = *pSrc;
  6887.             }
  6888.         }
  6889.  
  6890.         pSrc++;
  6891.         pDest++;
  6892.     }
  6893.     *pDest = 0;
  6894.     return pBuf;        
  6895. }
  6896.  
  6897. void CEditTextElement::ClearFormatting(){
  6898.     if ( m_tf & TF_HREF) {
  6899.         if ( m_href != ED_LINK_ID_NONE ) {
  6900.             m_href->Release();
  6901.         }
  6902.         else {
  6903.             XP_ASSERT(FALSE);
  6904.         }
  6905.     }
  6906.     m_href = ED_LINK_ID_NONE;
  6907.     m_tf = 0;
  6908.     SetFontSize( GetDefaultFontSize() );
  6909.     m_color = ED_Color::GetUndefined();
  6910.     m_iWeight = ED_FONT_WEIGHT_NORMAL;
  6911.     m_iPointSize = ED_FONT_POINT_SIZE_DEFAULT;
  6912.  
  6913.     /* We don't preserve the script extra because the SCRIPT attribute is cleared.
  6914.      */
  6915.     SetScriptExtra(NULL);
  6916. }
  6917.  
  6918. void CEditTextElement::SetData( EDT_CharacterData *pData ){
  6919.  
  6920.     // Either Java Script type
  6921.     ED_TextFormat tfJava = TF_SERVER | TF_SCRIPT | TF_STYLE;
  6922.  
  6923.     // if we are setting server or script,
  6924.     //   then it is the only thing we need to do
  6925.     if( (pData->mask & pData->values ) & tfJava ){
  6926.         ClearFormatting();
  6927. #ifdef USE_SCRIPT
  6928.         m_tf = (pData->mask & pData->values ) & tfJava;
  6929.         return;
  6930. #else
  6931.         XP_ASSERT(FALSE); // Front ends should not be setting these styles any more.
  6932.         return;
  6933. #endif
  6934.     }
  6935. #ifdef USE_SCRIPT
  6936.     // If we are already a Java Script type,
  6937.     //   and we are NOT removing that type (mask bit is not set)
  6938.     //   then ignore all other attributes
  6939.     if( (0 != (m_tf & TF_SERVER) && 
  6940.          0 == (pData->mask & TF_SERVER)) ||
  6941.         (0 != (m_tf & TF_STYLE) && 
  6942.          0 == (pData->mask & TF_STYLE)) ||
  6943.         (0 != (m_tf & TF_SCRIPT) && 
  6944.          0 == (pData->mask & TF_SCRIPT)) ){
  6945.         return;
  6946.     }
  6947. #endif
  6948.  
  6949.     if( pData->mask & TF_HREF ){
  6950.         SetHREF( pData->linkId );
  6951.     }
  6952.     if( pData->mask & TF_FONT_COLOR ){
  6953.         if( (pData->values & TF_FONT_COLOR) ){
  6954.             SetColor( EDT_LO_COLOR(pData->pColor) );
  6955.         }
  6956.         else {
  6957.             SetColor( ED_Color::GetUndefined() );
  6958.         }
  6959.     }
  6960.     if( pData->mask & TF_FONT_SIZE ){
  6961.         if( pData->values & TF_FONT_SIZE ){
  6962.             SetFontSize( pData->iSize );
  6963.         }
  6964.         else {
  6965.             SetFontSize( GetDefaultFontSize() );
  6966.             XP_ASSERT( (m_tf & TF_FONT_SIZE) == 0 );
  6967.         }
  6968.     }
  6969.  
  6970.     if( pData->mask & TF_FONT_FACE ){
  6971.         if( (pData->values & TF_FONT_FACE) ){
  6972.             SetFontFace( pData->pFontFace );
  6973.         }
  6974.         else {
  6975.             SetFontFace( 0 );
  6976.         }
  6977.     }
  6978.  
  6979.     if( pData->mask & TF_FONT_WEIGHT ){
  6980.         if( (pData->values & TF_FONT_WEIGHT) ){
  6981.             SetFontWeight( pData->iWeight );
  6982.         }
  6983.         else {
  6984.             SetFontWeight( ED_FONT_WEIGHT_NORMAL );
  6985.         }
  6986.     }
  6987.  
  6988.     if( pData->mask & TF_FONT_POINT_SIZE ){
  6989.         if( (pData->values & TF_FONT_POINT_SIZE) ){
  6990.             SetFontPointSize( pData->iPointSize );
  6991.         }
  6992.         else {
  6993.             SetFontPointSize( ED_FONT_POINT_SIZE_DEFAULT );
  6994.         }
  6995.     }
  6996.  
  6997.     // remove the elements we've already handled.
  6998.     ED_TextFormat mask = pData->mask & ~( TF_HREF 
  6999.                                         | TF_FONT_SIZE
  7000.                                         | TF_FONT_COLOR
  7001.                                         | TF_FONT_FACE
  7002.                                         | TF_FONT_WEIGHT
  7003.                                         | TF_FONT_POINT_SIZE
  7004. #ifdef USE_SCRIPT
  7005.                                         | TF_SCRIPT
  7006.                                         | TF_SERVER
  7007.                                         | TF_STYLE );
  7008. #else
  7009.                                         );
  7010. #endif
  7011.  
  7012.     ED_TextFormat values = pData->values & mask;
  7013.  
  7014.     m_tf = (m_tf & ~mask) | values;
  7015. }
  7016.  
  7017. EDT_CharacterData* CEditTextElement::GetData(){
  7018.     EDT_CharacterData *pData = EDT_NewCharacterData();
  7019.     pData->mask = -1;
  7020.     pData->values = m_tf;
  7021.     pData->iSize = GetFontSize();
  7022.     if( m_tf & TF_FONT_COLOR ){
  7023.         pData->pColor = edt_MakeLoColor( GetColor() );
  7024.     }
  7025.     if( m_tf & TF_HREF ){
  7026.         if( m_href != ED_LINK_ID_NONE ){
  7027.             pData->pHREFData = m_href->GetData();
  7028.         }
  7029.         pData->linkId = m_href;
  7030.     }
  7031.     if( m_tf & TF_FONT_FACE ){
  7032.         XP_ASSERT( m_pFace );
  7033.         pData->pFontFace = XP_STRDUP( m_pFace );
  7034.     }
  7035.     if ( m_tf & TF_FONT_WEIGHT ){
  7036.         pData->iWeight = m_iWeight;
  7037.     }
  7038.     if ( m_tf & TF_FONT_POINT_SIZE ){
  7039.         pData->iPointSize = m_iPointSize;
  7040.     }
  7041.     return pData;
  7042. }
  7043.  
  7044. void CEditTextElement::MaskData( EDT_CharacterData*& pData ){
  7045.     if( pData == 0 ){
  7046.         pData = GetData();
  7047.         return;
  7048.     }
  7049.     ED_TextFormat differences = pData->values ^ m_tf;
  7050.     if( differences ){
  7051.         // We are certain about all bits that are NOT different
  7052.         pData->mask &= ~differences;
  7053.     }
  7054.     // If mask bit is still set, then we THINK the attribute
  7055.     //  is the same, but we don't know until we check the
  7056.     //  color, href size, or font face directly 
  7057.     if( (pData->mask & pData->values & m_tf & TF_FONT_COLOR) 
  7058.                 && EDT_LO_COLOR( pData->pColor ) != GetColor() ){
  7059.         pData->mask &= ~TF_FONT_COLOR;
  7060.         XP_FREE( pData->pColor );
  7061.         pData->pColor = 0;
  7062.     }
  7063.     if( (pData->mask & pData->values & m_tf & TF_HREF)
  7064.                 && (pData->linkId != m_href ) ){
  7065.         pData->mask &= ~TF_HREF;
  7066.     }
  7067.     if( (pData->mask & pData->values & m_tf & TF_FONT_SIZE)
  7068.                 && (pData->iSize != m_iFontSize ) ){
  7069.         pData->mask &= ~TF_FONT_SIZE;
  7070.     }
  7071.     if( (pData->mask & pData->values & m_tf & TF_FONT_FACE) ){
  7072.         // Both have font face -- see if it is different
  7073.         char * pFontFace = GetFontFace();
  7074.         
  7075.         // Note: String shouldn't be empty if bit is set!
  7076.         XP_ASSERT(pData->pFontFace);
  7077.         if( !pData->pFontFace && !pFontFace ){
  7078.             // Both are empty so they are the same
  7079.             return;
  7080.         }
  7081.         if( pData->pFontFace
  7082.                 && (!pFontFace || XP_STRCMP(pData->pFontFace, pFontFace )) ){
  7083.             // Font name is different
  7084.             pData->mask &= ~TF_FONT_FACE;
  7085.         }
  7086.     }
  7087. }
  7088.  
  7089.  
  7090. //
  7091. // insert a character into the text buffer. At some point we should make sure we
  7092. //  don't insert two spaces together.
  7093. //
  7094. XP_Bool CEditTextElement::InsertChar( int iOffset, int newChar ){
  7095.     char buffer[2];
  7096.     buffer[0] = newChar;
  7097.     buffer[1] = '\0';
  7098.     return InsertChars(iOffset, buffer);
  7099. }
  7100.  
  7101. int32 CEditTextElement::InsertChars( int iOffset, char* pNewChars ){
  7102.     // This is in bytes, not in characters.
  7103.     // However, it assumes we've gotten whole characters.
  7104.     int32 iNumBytes = pNewChars ? XP_STRLEN(pNewChars) : 0;
  7105.     if ( iNumBytes <= 0 ) {
  7106.         return 0;
  7107.     }
  7108.     // guarantee the buffer size
  7109.     //
  7110.     int32 iOldLen = GetLen();
  7111.     int32 iSize = iOldLen  + iNumBytes;
  7112.     if( iSize + 1 >= m_textSize ){
  7113.         int newSize = GROW_TEXT( iSize + 1 );
  7114.         char *pNewBuf = (char*)XP_ALLOC( newSize );
  7115.         if( pNewBuf == 0 ){
  7116.             // out of memory, should probably throw.
  7117.             return 0; 
  7118.         }
  7119.         if( m_pText ){
  7120.             XP_STRCPY( pNewBuf, m_pText );
  7121.             XP_FREE( m_pText );
  7122.         }
  7123.         else {
  7124.             pNewBuf[0] = 0;
  7125.         }
  7126.         m_pText = pNewBuf;
  7127.         m_textSize = newSize;
  7128.     }
  7129.  
  7130.     //
  7131.     // create an empty space in the string.  We have to start from the end of 
  7132.     //  the string and move backward. Move the null, too
  7133.     //
  7134.     int32 i;
  7135.     for(i = iOldLen; i >= iOffset; i--){
  7136.         m_pText[i+iNumBytes] = m_pText[i];
  7137.     }
  7138.     // Insert the new text into the string.
  7139.     for(i = 0; i < iNumBytes; i++){
  7140.         m_pText[iOffset+i] = pNewChars[i];
  7141.     }
  7142.     
  7143.     /* update the text block with the new text */
  7144.     if ( m_pTextBlock != NULL ){
  7145.         lo_ChangeText ( m_pTextBlock, GetTextWithConvertedSpaces() );
  7146.     }
  7147.     
  7148.     return iNumBytes;
  7149. }
  7150.  
  7151. void CEditTextElement::DeleteChar( MWContext *pContext, int iOffset ){
  7152.     
  7153.     INTL_CharSetInfo csi = LO_GetDocumentCharacterSetInfo(pContext);
  7154.     int i = iOffset;
  7155.     char c = 1;
  7156.     int clen;
  7157.  
  7158.     clen = INTL_CharLen(INTL_GetCSIWinCSID(csi), (unsigned char *)m_pText+iOffset);
  7159.  
  7160.     if( m_pText[iOffset] ){
  7161.         while( c != 0 ){
  7162.             m_pText[i] = c = m_pText[i+clen];
  7163.             i++;
  7164.         }
  7165.     }
  7166. }
  7167.  
  7168. char* CEditTextElement::DebugFormat(){
  7169.     static char buf[1024];
  7170.     strcpy(buf, "( " );
  7171.     int i;
  7172.     if(m_tf & TF_BOLD)         strcat(buf,"B ");
  7173.     if(m_tf & TF_ITALIC)       strcat(buf,"I ");
  7174.     if(m_tf & TF_FIXED)        strcat(buf,"TT ");
  7175.     if(m_tf & TF_SUPER)        strcat(buf,"SUP ");
  7176.     if(m_tf & TF_SUB)          strcat(buf,"SUB ");
  7177.     if(m_tf & TF_STRIKEOUT)    strcat(buf,"SO ");
  7178.     if(m_tf & TF_UNDERLINE)    strcat(buf,"U ");
  7179.     if(m_tf & TF_BLINK)        strcat(buf,"BL ");
  7180. #ifdef USE_SCRIPT
  7181.     if(m_tf & TF_SERVER)       strcat(buf,"SERVER ");
  7182.     if(m_tf & TF_SCRIPT)       strcat(buf,"SCRIPT ");
  7183.     if(m_tf & TF_STYLE)        strcat(buf,"STYLE ");
  7184. #endif
  7185.     if(m_tf & TF_SPELL)        strcat(buf,"SPELL ");
  7186.     if(m_tf & TF_INLINEINPUT)  strcat(buf,"INLINEINPUT ");
  7187.     if(m_tf & TF_INLINEINPUTTHICK)  strcat(buf,"INLINEINPUTTHICK ");
  7188.     if(m_tf & TF_INLINEINPUTDOTTED)  strcat(buf,"INLINEINPUTDOTTED ");
  7189.  
  7190.     if(m_pScriptExtra){
  7191.         strcat(buf, m_pScriptExtra);
  7192.         strcat(buf," ");
  7193.     }
  7194.  
  7195.     if ( m_tf & TF_FONT_FACE && m_pFace ) {
  7196.         strcat( buf, m_pFace );
  7197.         strcat( buf, " ");
  7198.     }
  7199.  
  7200.     if ( m_tf & TF_FONT_WEIGHT ){
  7201.         char sizeBuf[30];
  7202.         PR_snprintf(sizeBuf, 30, "weight=%d ", m_iWeight );
  7203.         strcat( buf, sizeBuf);
  7204.     }
  7205.  
  7206.     if ( m_tf & TF_FONT_POINT_SIZE ){
  7207.         char sizeBuf[30];
  7208.         PR_snprintf(sizeBuf, 30, "pointsize=%d ", m_iPointSize );
  7209.         strcat( buf, sizeBuf);
  7210.     }
  7211.  
  7212.     if( m_tf & TF_FONT_COLOR ){ 
  7213.         char color_str[8];
  7214.         PR_snprintf(color_str, 8, "#%06lX ", GetColor().GetAsLong() );
  7215.         strcat( buf, color_str );
  7216.     }
  7217.  
  7218.     if( m_tf & TF_FONT_SIZE ){
  7219.         i = XP_STRLEN(buf);
  7220.         buf[i] = (GetFontSize() < 3  ? '-': '+');
  7221.         buf[i+1] = '0' + abs(GetFontSize() - 3);
  7222.         buf[i+2] = ' ';
  7223.         buf[i+3] = 0;
  7224.     }
  7225.  
  7226.     if( m_tf & TF_HREF ){ 
  7227.         strcat( buf, m_href->hrefStr );
  7228.     }
  7229.  
  7230.     strcat(buf, ") " );
  7231.     return buf;
  7232. }   
  7233.  
  7234. //
  7235. // Create a list of tags that represents the character formatting for this
  7236. //  text element.
  7237. //
  7238. void CEditTextElement::FormatOpenTags(PA_Tag*& pStart, PA_Tag*& pEnd){
  7239.  
  7240.  
  7241.     if(m_tf & TF_BOLD){ edt_AddTag( pStart, pEnd, P_BOLD, FALSE ); }
  7242.     if(m_tf & TF_ITALIC){ edt_AddTag( pStart, pEnd, P_ITALIC, FALSE ); }
  7243.     if(m_tf & TF_FIXED){ edt_AddTag( pStart, pEnd, P_FIXED, FALSE ); }
  7244.     if(m_tf & TF_SUPER){ edt_AddTag( pStart,pEnd, P_SUPER, FALSE ); }
  7245.     if(m_tf & TF_SUB){ edt_AddTag( pStart,pEnd, P_SUB, FALSE ); }
  7246.     if(m_tf & TF_NOBREAK){ edt_AddTag( pStart,pEnd, P_NOBREAK, FALSE ); }
  7247.     if(m_tf & TF_STRIKEOUT){ edt_AddTag( pStart,pEnd, P_STRIKEOUT, FALSE ); }
  7248.     if(m_tf & TF_UNDERLINE){ edt_AddTag( pStart,pEnd, P_UNDERLINE, FALSE ); }
  7249.     if(m_tf & TF_BLINK){ edt_AddTag( pStart,pEnd, P_BLINK, FALSE ); }
  7250.     if(m_tf & TF_SPELL){ edt_AddTag( pStart,pEnd, P_SPELL, FALSE ); }
  7251.     if(m_tf & TF_INLINEINPUT){ edt_AddTag( pStart,pEnd, P_INLINEINPUT, FALSE ); }
  7252.     if(m_tf & TF_INLINEINPUTTHICK){ edt_AddTag( pStart,pEnd, P_INLINEINPUTTHICK, FALSE ); }
  7253.     if(m_tf & TF_INLINEINPUTDOTTED){ edt_AddTag( pStart,pEnd, P_INLINEINPUTDOTTED, FALSE ); }
  7254.  
  7255.     // Face and color have to come before size for old browsers.
  7256.     if( m_tf & TF_FONT_FACE ){
  7257.         char buf[200];
  7258.         XP_STRCPY( buf, " FACE=\"" );
  7259.         strcat( buf, m_pFace );
  7260.         strcat( buf, "\">" );
  7261.         edt_AddTag( pStart,pEnd, P_FONT, FALSE, buf );
  7262.     }
  7263.  
  7264.     if ( m_tf & TF_FONT_WEIGHT ) {
  7265.         char buf[20];
  7266.         PR_snprintf(buf, 20, " FONT-WEIGHT=%d>", m_iWeight);
  7267.         edt_AddTag( pStart,pEnd, P_FONT, FALSE, buf );
  7268.     }
  7269.  
  7270.     if ( m_tf & TF_FONT_POINT_SIZE ) {
  7271.         char buf[20];
  7272.         PR_snprintf(buf, 20, " POINT-SIZE=%d>", m_iPointSize);
  7273.         edt_AddTag( pStart,pEnd, P_FONT, FALSE, buf );
  7274.     }
  7275.  
  7276.     if( GetColor().IsDefined() ){ 
  7277.         char buf[20];
  7278.         PR_snprintf(buf, 20, "COLOR=\"#%06lX\">", GetColor().GetAsLong() );
  7279.         edt_AddTag( pStart,pEnd, P_FONT, FALSE, buf );
  7280.     }
  7281.  
  7282.     if( m_tf & TF_FONT_SIZE ){
  7283.         char buf[20];
  7284.         XP_STRCPY( buf, " SIZE=" );
  7285.         int i = XP_STRLEN(buf);
  7286.         buf[i] = (GetFontSize() < 3  ? '-': '+');
  7287.         buf[i+1] = '0' + abs(GetFontSize() - 3);
  7288.         buf[i+2] = '>';
  7289.         buf[i+3] = 0;
  7290.         edt_AddTag( pStart,pEnd, P_FONT, FALSE, buf );
  7291.     }
  7292.  
  7293.     if( m_tf & TF_HREF){ 
  7294.         char *pBuf = PR_smprintf("HREF=%s %s\">", edt_MakeParamString(m_href->hrefStr),
  7295.             (m_href->pExtra ? m_href->pExtra : "") );        
  7296.         edt_AddTag( pStart,pEnd, P_ANCHOR, FALSE, pBuf );
  7297.         free( pBuf );
  7298.     }
  7299.  
  7300.     // if EVAL, hack the character attributes...
  7301. #ifdef USE_SCRIPT
  7302.     if(m_tf & TF_SERVER){ 
  7303.         edt_AddTag( pStart, pEnd, P_SERVER, FALSE );
  7304.         return;
  7305.     }
  7306.  
  7307.     if(m_tf & TF_SCRIPT){ 
  7308.         edt_AddTag( pStart, pEnd, P_SCRIPT, FALSE );
  7309.         return;
  7310.     }
  7311.  
  7312.     if(m_tf & TF_STYLE){
  7313.         edt_AddTag( pStart, pEnd, P_STYLE, FALSE );
  7314.         return;
  7315.     }
  7316. #endif
  7317. }
  7318.  
  7319. void CEditTextElement::FormatTransitionTags(CEditTextElement* /* pNext */,
  7320.             PA_Tag*& /* pStart */, PA_Tag*& /* pEnd */){
  7321. }
  7322.  
  7323. void CEditTextElement::FormatCloseTags(PA_Tag*& pStart, PA_Tag*& pEnd){
  7324.  
  7325.     // WARNING: order here is real important, Open and Close must match
  7326.     //  exactly.
  7327.  
  7328.     // if EVAL must be last
  7329. #ifdef USE_SCRIPT
  7330.     if(m_tf & TF_STYLE){ 
  7331.         edt_AddTag( pStart, pEnd, P_STYLE, TRUE );
  7332.     }
  7333.  
  7334.     if(m_tf & TF_SCRIPT){ 
  7335.         edt_AddTag( pStart, pEnd, P_SCRIPT, TRUE );
  7336.     }
  7337.  
  7338.     if(m_tf & TF_SERVER){ 
  7339.         edt_AddTag( pStart, pEnd, P_SERVER, TRUE );
  7340.     }
  7341. #endif
  7342.  
  7343.     //if(m_href){ edt_AddTag( pStart,pEnd, P_ITALIC, FALSE ); }
  7344.     if( m_tf & TF_HREF ){ edt_AddTag( pStart,pEnd, P_ANCHOR, TRUE );}
  7345.     if( m_tf & TF_FONT_SIZE ){edt_AddTag( pStart,pEnd, P_FONT, TRUE );}
  7346.     if( GetColor().IsDefined() ){edt_AddTag( pStart,pEnd, P_FONT, TRUE );}
  7347.     if( m_tf & TF_FONT_POINT_SIZE ) edt_AddTag( pStart,pEnd, P_FONT, TRUE );
  7348.     if( m_tf & TF_FONT_WEIGHT ) edt_AddTag( pStart,pEnd, P_FONT, TRUE );
  7349.     if( m_tf & TF_FONT_FACE ) edt_AddTag( pStart,pEnd, P_FONT, TRUE );
  7350.  
  7351.     if(m_tf & TF_INLINEINPUTDOTTED ){ edt_AddTag( pStart,pEnd, P_INLINEINPUTDOTTED, TRUE); }
  7352.     if(m_tf & TF_INLINEINPUTTHICK ){ edt_AddTag( pStart,pEnd, P_INLINEINPUTTHICK, TRUE); }
  7353.     if(m_tf & TF_INLINEINPUT ){ edt_AddTag( pStart,pEnd, P_INLINEINPUT, TRUE); }
  7354.     if(m_tf & TF_SPELL ){ edt_AddTag( pStart,pEnd, P_SPELL, TRUE); }
  7355.     if(m_tf & TF_BLINK ){ edt_AddTag( pStart,pEnd, P_BLINK, TRUE); }
  7356.     if(m_tf & TF_STRIKEOUT ){ edt_AddTag( pStart,pEnd, P_STRIKEOUT, TRUE); }
  7357.     if(m_tf & TF_UNDERLINE ){ edt_AddTag( pStart,pEnd, P_UNDERLINE, TRUE); }
  7358.     if(m_tf & TF_NOBREAK ){ edt_AddTag( pStart,pEnd, P_NOBREAK, TRUE); }
  7359.     if(m_tf & TF_SUB ){ edt_AddTag( pStart,pEnd, P_SUB, TRUE); }
  7360.     if(m_tf & TF_SUPER ){ edt_AddTag( pStart,pEnd, P_SUPER, TRUE); }
  7361.     if(m_tf & TF_FIXED ){ edt_AddTag( pStart, pEnd, P_FIXED, TRUE); }
  7362.     if(m_tf & TF_ITALIC ){ edt_AddTag( pStart, pEnd, P_ITALIC, TRUE); }
  7363.     if(m_tf & TF_BOLD ){ edt_AddTag( pStart, pEnd, P_BOLD, TRUE); }
  7364.  
  7365.  
  7366. }
  7367.  
  7368. //
  7369. // Create a text tag.  We are going to be feeding these tags to the layout 
  7370. //  engine.  The layout element this edit element points to is going to change.
  7371. //
  7372. PA_Tag* CEditTextElement::TagOpen( int iEditOffset ){
  7373.     // LTNOTE: I think that we only want to do this if we are the first text
  7374.     //  in the container, other wise, we don't do anything.
  7375.     PA_Tag *pStart = 0;
  7376.     PA_Tag *pEnd = 0;
  7377.     XP_Bool bFirst = IsFirstInContainer();
  7378.  
  7379.     if( GetLen() == 0 && (!bFirst || (bFirst && LeafInContainerAfter() != 0))){
  7380.         return 0;
  7381.     }
  7382.  
  7383.     PA_Tag *pTag = XP_NEW( PA_Tag );
  7384.     XP_BZERO( pTag, sizeof( PA_Tag ) );
  7385.  
  7386.     FormatOpenTags( pStart, pEnd );    
  7387.  
  7388.     if( pStart == 0 ){
  7389.         pStart = pTag;
  7390.     }
  7391.     if( pEnd ){
  7392.         pEnd->next = pTag;
  7393.     }
  7394.     pEnd = pTag;
  7395.  
  7396.     if(GetLen() == 0){
  7397.         // As of 3.0b5, a non-breaking space always caused a memory leak when
  7398.         // it was laid out. See bug 23404
  7399.         // So use a different character here. The character is
  7400.         // removed by code in CEditBuffer::Relayout
  7401. #ifdef USE_PERIOD_FOR_BLANK_LAYOUT
  7402.         SetTagData(pTag, ".");
  7403. #else
  7404.         SetTagData(pTag, NON_BREAKING_SPACE_STRING);
  7405. #endif
  7406.         //SetTagData(pTag, "");
  7407.     }
  7408.     else {
  7409.         SetTagData(pTag, GetTextWithConvertedSpaces()+iEditOffset);
  7410.     }
  7411.     if( iEditOffset==0 ){
  7412.         m_pFirstLayoutElement = 0;
  7413.     }
  7414.  
  7415.     FormatCloseTags( pStart, pEnd );
  7416.  
  7417.     return pStart;
  7418. }
  7419.  
  7420. XP_Bool CEditTextElement::GetLOTextAndOffset( ElementOffset iEditOffset, XP_Bool bStickyAfter, LO_TextStruct*&  pRetText, 
  7421.         int& iLayoutOffset ){
  7422.     
  7423.     LO_TextStruct *pText;
  7424.     LO_Element* pLoElement = m_pFirstLayoutElement;
  7425.     XP_ASSERT(!pLoElement || pLoElement->lo_any.edit_offset == 0);
  7426.     intn len = GetLen();
  7427.  
  7428.     while(pLoElement != 0){
  7429.         if( pLoElement->type == LO_TEXT ){
  7430.             // if we have scanned past our edit element, we can't find the 
  7431.             // insert point.
  7432.             pText = &(pLoElement->lo_text);
  7433.             if( pText->edit_element != this){
  7434.                 /* assert(0)
  7435.                 */
  7436.                 return FALSE;
  7437.             }
  7438.  
  7439.             int16 textLen = pText->text_len;
  7440.             int32 loEnd = pText->edit_offset + textLen;
  7441.  
  7442.             //
  7443.             // See if we are at the dreaded lopped of the last space case.
  7444.             //
  7445.             if( loEnd+1 == iEditOffset ){
  7446.                 // ok, we've scaned past the end of the Element
  7447.                 if( len == iEditOffset && m_pText[iEditOffset-1] == ' '){
  7448.                     // we wraped to the next element.  Return the next Text Element.
  7449.                     CEditElement *pNext = FindNextElement(&CEditElement::FindText,0);
  7450.                     if( pNext ){
  7451.                         return pNext->Text()->GetLOTextAndOffset( 0, FALSE, pRetText, iLayoutOffset );
  7452.                     }
  7453.  
  7454.                     // we should have all these cases handled now.
  7455.  
  7456.                     // jhp Nope. (a least as of Beta 2) If you have a very narrow document,
  7457.                     // and you
  7458.                     // type in two lines of text, and you're at the end of the
  7459.                     // second line, and you type a space, and the
  7460.                     // space causes a line wrap, then you end up here.
  7461.                     // XP_ASSERT(FALSE);
  7462.  
  7463.                     return FALSE;
  7464.                 }
  7465.             }
  7466.  
  7467.             if( loEnd > iEditOffset || (loEnd == iEditOffset
  7468.                 && (!bStickyAfter || len == iEditOffset || textLen == 0 ) ) ){
  7469.                 // we've found the right element, return the information.
  7470.                 iLayoutOffset = iEditOffset - pText->edit_offset;
  7471.                 pRetText = pText;
  7472.                 return TRUE;
  7473.             }
  7474.             
  7475.         }
  7476.         pLoElement = pLoElement->lo_any.next;
  7477.     }
  7478.     return FALSE;
  7479. }
  7480.  
  7481. LO_TextStruct* CEditTextElement::GetLOText( int iEditOffset ){
  7482.     
  7483.     LO_TextStruct *pText;
  7484.     LO_Element* pLoElement = m_pFirstLayoutElement;
  7485.  
  7486.     while(pLoElement != 0){
  7487.         if( pLoElement->type == LO_TEXT ){
  7488.             // if we have scanned past our edit element, we can't find the 
  7489.             // insert point.
  7490.             pText = &(pLoElement->lo_text);
  7491.             if( pText->edit_element != this){
  7492.                 return 0;
  7493.             }
  7494.             if( pText->edit_offset > iEditOffset ){
  7495.                 return 0;
  7496.             }
  7497.  
  7498.             if( pText->edit_offset + pText->text_len >= iEditOffset ){
  7499.                 // we've found the right element, return the information.
  7500.                 return pText;
  7501.             }
  7502.             
  7503.         }
  7504.         pLoElement = pLoElement->lo_any.next;
  7505.     }
  7506.     return 0;
  7507. }
  7508.  
  7509. LO_TextBlock* CEditTextElement::GetTextBlock()
  7510. {
  7511.     return m_pTextBlock;
  7512. }
  7513.  
  7514.  
  7515. XP_Bool CEditTextElement::CanReflow() {
  7516.     // we can only reflow if we have a text block
  7517.     return m_pTextBlock != NULL;
  7518. }
  7519.  
  7520. void CEditTextElement::PrintTagOpen( CPrintState *pPrintState, TagType t, 
  7521.             ED_TextFormat tf, char* pExtra ){
  7522.     if ( ! pExtra ) pExtra = ">";
  7523.     pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "<%s%s", 
  7524.                 EDT_TagString(t), pExtra );
  7525.     pPrintState->m_formatStack.Push(tf);
  7526.     pPrintState->m_elementStack.Push(this);
  7527. }
  7528.  
  7529.  
  7530. void CEditTextElement::PrintFormatDifference( CPrintState *ps, 
  7531.         ED_TextFormat bitsDifferent ){
  7532.     if(bitsDifferent & TF_BOLD){ PrintTagOpen( ps, P_BOLD, TF_BOLD ); } 
  7533.     if(bitsDifferent & TF_ITALIC){ PrintTagOpen( ps, P_ITALIC, TF_ITALIC ); } 
  7534.     if(bitsDifferent & TF_FIXED){ PrintTagOpen( ps, P_FIXED, TF_FIXED ); } 
  7535.     if(bitsDifferent & TF_SUPER){ PrintTagOpen( ps, P_SUPER, TF_SUPER ); } 
  7536.     if(bitsDifferent & TF_SUB){ PrintTagOpen( ps, P_SUB, TF_SUB ); } 
  7537.     if(bitsDifferent & TF_NOBREAK){ PrintTagOpen( ps, P_NOBREAK, TF_NOBREAK ); } 
  7538.     if(bitsDifferent & TF_STRIKEOUT){ PrintTagOpen( ps, P_STRIKEOUT, TF_STRIKEOUT ); } 
  7539.     if(bitsDifferent & TF_UNDERLINE){ PrintTagOpen( ps, P_UNDERLINE, TF_UNDERLINE ); } 
  7540.     if(bitsDifferent & TF_BLINK){ PrintTagOpen( ps, P_BLINK, TF_BLINK ); } 
  7541.     if(bitsDifferent & TF_SPELL){ /*do nothing*/ } 
  7542.     if(bitsDifferent & TF_INLINEINPUT){ /*do nothing*/ } 
  7543.     if(bitsDifferent & TF_INLINEINPUTTHICK){ /*do nothing*/ } 
  7544.     if(bitsDifferent & TF_INLINEINPUTDOTTED){ /*do nothing*/ } 
  7545.  
  7546.     // Face and color have to preceed FONT_SIZE, or else old browsers will
  7547.     // reset the font_size to zero when they encounter the unknown tags.
  7548.  
  7549.     if( bitsDifferent & TF_FONT_FACE ){
  7550.         ps->m_iCharPos += ps->m_pOut->Printf( "<FONT FACE=\"%s\">", m_pFace);
  7551.         ps->m_formatStack.Push(TF_FONT_FACE);
  7552.         ps->m_elementStack.Push(this);
  7553.     }
  7554.  
  7555.     if( bitsDifferent & TF_FONT_WEIGHT ){
  7556.         ps->m_iCharPos += ps->m_pOut->Printf( "<FONT FONT-WEIGHT=\"%d\">", m_iWeight);
  7557.         ps->m_formatStack.Push(TF_FONT_WEIGHT);
  7558.         ps->m_elementStack.Push(this);
  7559.     }
  7560.  
  7561.     if( bitsDifferent & TF_FONT_POINT_SIZE ){
  7562.         ps->m_iCharPos += ps->m_pOut->Printf( "<FONT POINT-SIZE=\"%d\">", m_iPointSize);
  7563.         ps->m_formatStack.Push(TF_FONT_POINT_SIZE);
  7564.         ps->m_elementStack.Push(this);
  7565.     }
  7566.  
  7567.     if( bitsDifferent & TF_FONT_COLOR ){
  7568.         ps->m_iCharPos += ps->m_pOut->Printf( "<FONT COLOR=\"#%06lX\">", 
  7569.                     GetColor().GetAsLong() );
  7570.         ps->m_formatStack.Push(TF_FONT_COLOR);
  7571.         ps->m_elementStack.Push(this);
  7572.     }
  7573.  
  7574.     if( bitsDifferent & TF_FONT_SIZE ){
  7575.         char buf[4];
  7576.         buf[0] = (GetFontSize() < 3  ? '-': '+');
  7577.         buf[1] = '0' + abs(GetFontSize() - 3);
  7578.         buf[2] = 0;
  7579.         ps->m_iCharPos += ps->m_pOut->Printf( "<FONT SIZE=%s>", buf);
  7580.         ps->m_formatStack.Push(TF_FONT_SIZE);
  7581.         ps->m_elementStack.Push(this);
  7582.     }
  7583.  
  7584.     if( bitsDifferent & TF_HREF){ 
  7585.         // doesn't use the output routine because HREF can be large.
  7586.         char *pS1 = "";
  7587.         char *pS2 = "";
  7588.         if( m_href->pExtra ){
  7589.             pS1 = " ";
  7590.             pS2 = m_href->pExtra;
  7591.         }
  7592.         ps->m_iCharPos += ps->m_pOut->Printf( 
  7593.             "<A HREF=%s%s%s>", edt_MakeParamString(m_href->hrefStr), pS1, pS2 );
  7594.  
  7595.         ps->m_formatStack.Push(TF_HREF);
  7596.         ps->m_elementStack.Push(this);
  7597.     }
  7598.  
  7599. #ifdef USE_SCRIPT
  7600.     if(bitsDifferent & TF_STYLE){
  7601.         PrintTagOpen( ps, P_STYLE, TF_STYLE, GetScriptExtra() ); }
  7602.     if(bitsDifferent & TF_SCRIPT){
  7603.         PrintTagOpen( ps, P_SCRIPT, TF_SCRIPT, GetScriptExtra() ); } 
  7604.     if(bitsDifferent & TF_SERVER){ PrintTagOpen( ps, P_SERVER, TF_SERVER, GetScriptExtra() ); } 
  7605. #endif
  7606. }
  7607.  
  7608. void CEditTextElement::PrintFormat( CPrintState *ps, 
  7609.                                     CEditTextElement *pFirst, 
  7610.                                     ED_TextFormat mask ){
  7611.     ED_TextFormat bitsCommon = 0;
  7612.     ED_TextFormat bitsDifferent = 0;
  7613.  
  7614.     ComputeDifference(pFirst, mask, bitsCommon, bitsDifferent);
  7615.  
  7616.     CEditTextElement *pNext = (CEditTextElement*) TextInContainerAfter();
  7617.  
  7618.     if( bitsCommon ){
  7619.         // While we're in a run of the same style, there's nothing to do.
  7620.         // Avoid unnescessary recursion so we don't blow the stack on Mac or Win16
  7621.         while ( pNext && SameAttributes(pNext) ){
  7622.             pNext = (CEditTextElement*) pNext->TextInContainerAfter();
  7623.         }
  7624.         if( pNext ){
  7625.             pNext->PrintFormat( ps, pFirst, bitsCommon );
  7626.         }
  7627.         else {
  7628.             // we hit the end, so we have to open everything
  7629.             pFirst->PrintFormatDifference( ps, bitsCommon);
  7630.         }
  7631.     }
  7632.     if( bitsDifferent ){
  7633.         pFirst->PrintFormatDifference( ps, bitsDifferent );
  7634.     }
  7635. }
  7636.  
  7637. XP_Bool CEditTextElement::SameAttributes(CEditTextElement *pCompare){
  7638.     ED_TextFormat bitsCommon;
  7639.     ED_TextFormat bitsDifferent;
  7640.     ComputeDifference(pCompare, -1, bitsCommon, bitsDifferent);
  7641.     return ( bitsCommon == m_tf);
  7642. }
  7643.  
  7644. void CEditTextElement::ComputeDifference(CEditTextElement *pFirst, 
  7645.         ED_TextFormat mask, ED_TextFormat& bitsCommon, ED_TextFormat& bitsDifferent){
  7646.     bitsCommon = m_tf & mask;
  7647.     bitsDifferent = mask & ~bitsCommon;
  7648.  
  7649.     if( (bitsCommon & TF_FONT_SIZE)&& (GetFontSize() != pFirst->GetFontSize())){
  7650.         bitsCommon &= ~TF_FONT_SIZE;
  7651.         bitsDifferent |= TF_FONT_SIZE;
  7652.     }
  7653.  
  7654.     if( (bitsCommon & TF_FONT_COLOR)&& (GetColor() != pFirst->GetColor())){
  7655.         bitsCommon &= ~TF_FONT_COLOR;
  7656.         bitsDifferent |= TF_FONT_COLOR;
  7657.     }
  7658.  
  7659.     if( (bitsCommon & TF_FONT_POINT_SIZE)&& (GetFontPointSize() != pFirst->GetFontPointSize())){
  7660.         bitsCommon &= ~TF_FONT_POINT_SIZE;
  7661.         bitsDifferent |= TF_FONT_POINT_SIZE;
  7662.     }
  7663.  
  7664.     if( (bitsCommon & TF_FONT_WEIGHT)&& (GetFontWeight() != pFirst->GetFontWeight())){
  7665.         bitsCommon &= ~TF_FONT_WEIGHT;
  7666.         bitsDifferent |= TF_FONT_WEIGHT;
  7667.     }
  7668.  
  7669.     //CLM: These pointers were sometimes NULL
  7670.     char * pFontFace = GetFontFace();
  7671.     char * pFirstFontFace = pFirst->GetFontFace();
  7672.     // Assure that we have strings to compare for font face
  7673.     if( !pFontFace ){
  7674.         pFontFace = " ";
  7675.     }
  7676.     if( !pFirstFontFace ){
  7677.         pFirstFontFace = " ";
  7678.     }
  7679.  
  7680.     if( (bitsCommon & TF_FONT_FACE) && XP_STRCMP(pFontFace,pFirstFontFace) != 0 ){
  7681.         bitsCommon &= ~TF_FONT_FACE;
  7682.         bitsDifferent |= TF_FONT_FACE;
  7683.     }
  7684.     if( bitsCommon & TF_FONT_FACE ) {
  7685.         char* a = GetFontFace();
  7686.         char* b = pFirst->GetFontFace();                 
  7687.         XP_Bool bA = a != NULL;
  7688.         XP_Bool bB = b != NULL;
  7689.         if ((bA ^ bB) ||
  7690.             (bA && bB && XP_STRCMP(a, b) != 0)) {
  7691.             bitsCommon &= ~TF_FONT_FACE;
  7692.             bitsDifferent |= TF_FONT_FACE;
  7693.         }
  7694.     }
  7695.     if( (bitsCommon & TF_HREF)&& (GetHREF() != pFirst->GetHREF())){
  7696.         bitsCommon &= ~TF_HREF;
  7697.         bitsDifferent |= TF_HREF;
  7698.     }
  7699. }
  7700.  
  7701. void CEditTextElement::PrintTagClose( CPrintState *ps, TagType t ){
  7702.     ps->m_iCharPos += ps->m_pOut->Printf( "</%s>", 
  7703.                 EDT_TagString(t) );
  7704. }
  7705.  
  7706. void CEditTextElement::PrintPopFormat( CPrintState *ps, int iStackTop ){
  7707.  
  7708.     while( ps->m_formatStack.m_iTop >= iStackTop ){
  7709.         
  7710.         ED_TextFormat tf = (ED_TextFormat) ps->m_formatStack.Pop();
  7711.         ps->m_elementStack.Pop();
  7712. #ifdef USE_SCRIPT
  7713.         if( tf & TF_STYLE){ PrintTagClose( ps, P_STYLE ); } 
  7714.         if( tf & TF_SCRIPT){ PrintTagClose( ps, P_SCRIPT ); } 
  7715.         if( tf & TF_SERVER){ PrintTagClose( ps, P_SERVER ); } 
  7716. #endif
  7717.         if( tf & TF_BOLD){ PrintTagClose( ps, P_BOLD ); } 
  7718.         if( tf & TF_ITALIC){ PrintTagClose( ps, P_ITALIC ); } 
  7719.         if( tf & TF_FIXED){ PrintTagClose( ps, P_FIXED ); } 
  7720.         if( tf & TF_SUPER){ PrintTagClose( ps, P_SUPER ); } 
  7721.         if( tf & TF_SUB){ PrintTagClose( ps, P_SUB ); } 
  7722.         if( tf & TF_NOBREAK){ PrintTagClose( ps, P_NOBREAK ); } 
  7723.         if( tf & TF_STRIKEOUT){ PrintTagClose( ps, P_STRIKEOUT ); } 
  7724.         if( tf & TF_UNDERLINE){ PrintTagClose( ps, P_UNDERLINE ); } 
  7725.         if( tf & TF_BLINK){ PrintTagClose( ps, P_BLINK ); } 
  7726.         if( tf & TF_SPELL){ /* do nothing */ } 
  7727.         if( tf & TF_INLINEINPUT){ /* do nothing */ } 
  7728.         if( tf & TF_INLINEINPUTTHICK){ /* do nothing */ } 
  7729.         if( tf & TF_INLINEINPUTDOTTED){ /* do nothing */ } 
  7730.         if( tf & TF_FONT_SIZE){ PrintTagClose( ps, P_FONT ); } 
  7731.         if( tf & TF_FONT_COLOR){ PrintTagClose( ps, P_FONT ); } 
  7732.         if( tf & TF_FONT_POINT_SIZE){ PrintTagClose( ps, P_FONT ); } 
  7733.         if( tf & TF_FONT_WEIGHT){ PrintTagClose( ps, P_FONT ); } 
  7734.         if( tf & TF_FONT_FACE){ PrintTagClose( ps, P_FONT ); } 
  7735.         if( tf & TF_HREF){ PrintTagClose( ps, P_ANCHOR ); } 
  7736.     }
  7737. }
  7738.  
  7739. ED_TextFormat CEditTextElement::PrintFormatClose( CPrintState *ps ){
  7740.     XP_Bool bDiff = FALSE;
  7741.     ED_TextFormat bitsNeedFormat = m_tf ;
  7742.  
  7743.     if( ps->m_formatStack.IsEmpty()){
  7744.         return bitsNeedFormat;        
  7745.     }
  7746.     int i = -1;
  7747.     while( !bDiff && ++i <= ps->m_formatStack.m_iTop ){
  7748.         ED_TextFormat tf = (ED_TextFormat) ps->m_formatStack[i];
  7749.         if( m_tf & tf ){
  7750.             if( tf == TF_FONT_COLOR 
  7751.                     && GetColor() != ps->m_elementStack[i]->GetColor()){
  7752.                 bDiff = TRUE;
  7753.             }
  7754.             else if( tf == TF_FONT_SIZE 
  7755.                     && GetFontSize() != ps->m_elementStack[i]->GetFontSize()){
  7756.                 bDiff = TRUE;
  7757.             }
  7758.             else if( tf == TF_FONT_POINT_SIZE
  7759.                     && GetFontPointSize() != ps->m_elementStack[i]->GetFontPointSize()){
  7760.                 bDiff = TRUE;
  7761.             }
  7762.             else if( tf == TF_FONT_WEIGHT
  7763.                     && GetFontWeight() != ps->m_elementStack[i]->GetFontWeight()){
  7764.                 bDiff = TRUE;
  7765.             }
  7766.             else if( tf == TF_FONT_FACE 
  7767.                     && XP_STRCMP(GetFontFace(), ps->m_elementStack[i]->GetFontFace()) != 0){
  7768.                 bDiff = TRUE;
  7769.             }
  7770.             else if( tf == TF_HREF
  7771.                     && GetHREF() != ps->m_elementStack[i]->GetHREF() ){
  7772.                 bDiff = TRUE;
  7773.             }
  7774.         }
  7775.         else {
  7776.             bDiff = TRUE;
  7777.         }
  7778.  
  7779.         // if the stuff is on the stack, then it doesn't need to be formatted
  7780.         if( !bDiff ){
  7781.             bitsNeedFormat &=  ~tf;
  7782.         }
  7783.     }
  7784.     PrintPopFormat( ps, i );
  7785.     return bitsNeedFormat;
  7786.  
  7787. }
  7788.  
  7789. // for use within 
  7790. void CEditTextElement::PrintWithEscapes( CPrintState *ps, XP_Bool bTrimTrailingSpaces ){
  7791.     char *p = GetTextWithConvertedSpaces();
  7792.     int csid = INTL_DefaultWinCharSetID(ps->m_pBuffer->m_pContext);
  7793.  
  7794.     // Trim the last non-breaking spaces at the end of a 
  7795.     // top-level paragraph. The spaces just irritate users,
  7796.     // especially in HTML mail.
  7797.     // (But don't trim the last nbsp if it's the only thing in the paragraph, or
  7798.     // else layout will ignore the paragraph.)
  7799.     // And don't trim them in tables, where they're used to affect column width.
  7800.  
  7801.     if ( bTrimTrailingSpaces && p && ! LeafInContainerAfter() && ! GetSubDocSkipRoot() ) {
  7802.         /* find last NBSP by going forward char-by-char */
  7803.         char *s, *last_nbsp, *end;
  7804.         s = last_nbsp = p;
  7805.         end = p + XP_STRLEN(p);
  7806.         while ((s <= end) && (*s)) {
  7807.             /* if not nbsp then advance both pointers */
  7808.             if (*s != NON_BREAKING_SPACE)
  7809.                 last_nbsp = s = INTL_NextChar(csid, s);
  7810.             else
  7811.                 s = INTL_NextChar(csid, s);
  7812.         }
  7813.         if (last_nbsp <= end){
  7814.             // Don't trim last nbsp if it's the only thing in the paragraph.
  7815.             if ( p == last_nbsp && IsFirstInContainer() && last_nbsp < end ){
  7816.                 last_nbsp++;
  7817.             }
  7818.             *last_nbsp = '\0'; /* points to last nbsp or end of string */
  7819.         }
  7820.     }
  7821.     if( p == 0 ){
  7822. #ifndef EDT_DDT
  7823.         p = NON_BREAKING_SPACE_STRING;
  7824. #endif
  7825.     }
  7826.     edt_PrintWithEscapes( ps, p, !InFormattedText() );
  7827. }
  7828.  
  7829. void CEditTextElement::PrintLiteral( CPrintState *ps ){
  7830.     int iLen = GetLen();
  7831.     ps->m_pOut->Write( GetText(), iLen );
  7832.     ps->m_iCharPos += iLen;
  7833. }
  7834.  
  7835. void CEditTextElement::PrintOpen( CPrintState *ps){
  7836.     ED_TextFormat bitsToFormat = PrintFormatClose( ps );
  7837.  
  7838.     CEditTextElement *pNext = (CEditTextElement*) TextInContainerAfter();
  7839.     if( pNext ){
  7840.         pNext->PrintFormat( ps, this, bitsToFormat );
  7841.     }
  7842.     else {
  7843.         PrintFormatDifference( ps, bitsToFormat );
  7844.     }
  7845.     
  7846.     if ( ps->ShouldPrintSelectionComments(this) ){
  7847.         // Up to three pieces
  7848.         int32 index = 0;
  7849.         if ( ps->ShouldPrintSelectionComment(this, FALSE) ){
  7850.             index = ps->m_selection.m_start.m_iPos;
  7851.             if ( 0 < index ) {
  7852.                 PrintRange(ps, 0, index);
  7853.             }
  7854.             ps->PrintSelectionComment(FALSE, ps->m_selection.m_start.m_bStickyAfter);
  7855.         }
  7856.         if ( ps->ShouldPrintSelectionComment(this, TRUE) ){
  7857.             int32 oldIndex = index;
  7858.             index = ps->m_selection.m_end.m_iPos;
  7859.             if ( oldIndex < index ) {
  7860.                 PrintRange(ps, oldIndex, index);
  7861.             }
  7862.             ps->PrintSelectionComment(TRUE, ps->m_selection.m_end.m_bStickyAfter);
  7863.         }
  7864.         if ( index < GetLen() ){
  7865.             PrintRange(ps, index, GetLen());
  7866.         }
  7867.     }
  7868.     else {
  7869.         PrintOpen2(ps, TRUE);
  7870.     }
  7871.  
  7872.     if( pNext == 0 ) {
  7873.         PrintPopFormat( ps, 0 );
  7874.     }
  7875. }
  7876.  
  7877. void CEditTextElement::PrintRange( CPrintState *ps, int32 start, int32 end ){
  7878.     // We modify m_pText in place and then call PrintOpen2 to do the
  7879.     // actual printing. (And then we restore m_pText to its original
  7880.     // condition.)
  7881.     //
  7882.     int32 len = GetLen();
  7883.     if ( start == 0 && end == len ){
  7884.         PrintOpen2(ps, FALSE);
  7885.     }
  7886.     else {
  7887.         if ( end > len ) {
  7888.             XP_ASSERT(FALSE);
  7889. #ifdef DEBUG_akkana
  7890.             printf("end = %d len = %d\n", end, len);
  7891. #endif
  7892.             end = len;
  7893.         }
  7894.         if ( start < 0 ) {
  7895.             XP_ASSERT(FALSE);
  7896.             start = 0;
  7897.         }
  7898.         if ( start < end && m_pText ) {
  7899.             char* pText = m_pText;
  7900.             char save = pText[end];
  7901.             pText[end] = '\0';
  7902.             m_pText = pText + start;
  7903.             // If GetLen() is ever cached we would need to update the cache.
  7904.             XP_ASSERT(GetLen() == end - start);
  7905.             PrintOpen2(ps, FALSE);
  7906.             m_pText = pText;
  7907.             pText[end] = save;
  7908.         }
  7909.     }
  7910. }
  7911.  
  7912. void CEditTextElement::PrintOpen2( CPrintState *ps, XP_Bool bTrimTrailingSpaces )
  7913. {
  7914. #ifdef USE_SCRIPT
  7915.     if( m_tf & (TF_SERVER|TF_SCRIPT|TF_STYLE) ){
  7916.         PrintLiteral( ps );
  7917.     }
  7918.     else {
  7919.         PrintWithEscapes( ps );
  7920.     }
  7921. #else
  7922.     PrintWithEscapes( ps, bTrimTrailingSpaces );
  7923. #endif
  7924. }
  7925.  
  7926. XP_Bool CEditTextElement::Reduce( CEditBuffer *pBuffer ){
  7927.     if( GetLen() == 0 ){
  7928.         if( pBuffer->m_pCurrent == this 
  7929.                 || (IsFirstInContainer()
  7930.                         && LeafInContainerAfter() == 0)
  7931.             ){
  7932.             return FALSE;
  7933.         }
  7934.         else {
  7935.             return TRUE;
  7936.         }
  7937.     }
  7938.     else {
  7939.         return FALSE;
  7940.     }
  7941. }
  7942.  
  7943. #ifdef DEBUG
  7944. void CEditTextElement::ValidateTree(){
  7945.     CEditLeafElement::ValidateTree();
  7946.     LO_TextStruct *pText = (LO_TextStruct*) GetLayoutElement();
  7947.  
  7948.     if( GetType() != P_TEXT  ){
  7949.         XP_ASSERT(FALSE);
  7950.     }
  7951.     if(m_pText && pText ) {
  7952.         if (pText->edit_element != this){
  7953.             XP_ASSERT(FALSE);
  7954.         }
  7955.         if (pText->edit_offset != 0){
  7956.             XP_ASSERT(FALSE);
  7957.         }
  7958.     }
  7959.     XP_Bool bHasLinkTag = (m_tf & TF_HREF) != 0;
  7960.     XP_Bool bHasLinkRef = (m_href != ED_LINK_ID_NONE);
  7961.     if( bHasLinkTag != bHasLinkRef ){
  7962.         XP_ASSERT(FALSE);
  7963.     };
  7964. }
  7965. #endif    //    DEBUG
  7966.  
  7967. void CEditTextElement::StreamToPositionalText( IStreamOut *pOut, XP_Bool bEnd ){ 
  7968.     if( !bEnd ){
  7969.         char space = ' ';
  7970.  
  7971.         // Ignore Quoted text, unless there is a selection.
  7972.         if ( InMailQuote() && !EDT_IsSelected(GetEditBuffer()->m_pContext)) {
  7973.             // Write out spaces instead of text. This prevents us from spell checking
  7974.             // quoted text.
  7975.             int32 len = GetLen();
  7976.             for(int32 i = 0; i < len; i++){
  7977.                 pOut->Write(&space, 1);
  7978.             }
  7979.         }
  7980.         else {
  7981.             INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(GetEditBuffer()->m_pContext);
  7982.             int16   csid = INTL_GetCSIWinCSID(c);
  7983.  
  7984.             if (INTL_CharSetType(csid) == SINGLEBYTE){
  7985.                 pOut->Write(GetText(),GetLen());
  7986.             } 
  7987.             else{
  7988.                 // Replace any multi-byte characters with spaces, to avoid spell checking
  7989.                 // of Japanese and other far-eastern text.
  7990.                 int32 textLen, charLen=0;
  7991.                 char *pText = GetText();
  7992.  
  7993.                 for (textLen = GetLen(); textLen > 0; textLen -= charLen, pText += charLen){
  7994.                     charLen = INTL_CharLen(csid, (unsigned char *)pText);
  7995.                     if (charLen > 1){
  7996.                         for(int32 i = 0; i < charLen; i++){
  7997.                             pOut->Write(&space, 1);
  7998.                         }
  7999.                     }
  8000.                     else{
  8001.                         pOut->Write(pText, charLen);
  8002.                     }
  8003.                 }
  8004.             }
  8005.         }
  8006.     }
  8007. }
  8008.  
  8009.  
  8010. //-----------------------------------------------------------------------------
  8011. // Insertion routines.
  8012. //-----------------------------------------------------------------------------
  8013.  
  8014. //
  8015. // Split text object into two text objects.  Parent is the same.
  8016. //
  8017. CEditElement* CEditTextElement::SplitText( int iOffset ){
  8018.     CEditTextElement* pNew = new CEditTextElement( 0, m_pText+iOffset );
  8019.     m_pText[iOffset] = 0;
  8020.     GetParent()->Split( this, pNew, &CEditElement::SplitContainerTest, 0 );
  8021.     return  pNew;
  8022. }
  8023.  
  8024. CEditTextElement* CEditTextElement::CopyEmptyText( CEditElement *pParent ){
  8025.     CEditTextElement* pNew = new CEditTextElement(pParent, 0);
  8026.  
  8027.     pNew->m_tf = m_tf;
  8028.     pNew->m_iFontSize = m_iFontSize;
  8029.     pNew->m_color = m_color;
  8030.     if( (m_tf & TF_HREF) && m_href != ED_LINK_ID_NONE){
  8031.         pNew->SetHREF(m_href);
  8032.     }
  8033.     if ( m_pFace ) {
  8034.         pNew->SetFontFace(m_pFace);
  8035.     }
  8036.     pNew->m_iWeight = m_iWeight;
  8037.     pNew->m_iPointSize = m_iPointSize;
  8038.     if ( m_pScriptExtra ) {
  8039.         pNew->SetScriptExtra(m_pScriptExtra);
  8040.     }
  8041.     return pNew;
  8042. }
  8043.  
  8044. void CEditTextElement::DeleteText(){
  8045.     CEditElement *pKill = this;
  8046.     CEditElement *pParent;
  8047.  
  8048.     do {
  8049.         pParent = pKill->GetParent();
  8050.         pKill->Unlink();
  8051.         delete pKill;
  8052.         pKill = pParent;
  8053.     } while( BitSet( edt_setCharFormat, pKill->GetType() ) && pKill->GetChild() == 0 );
  8054. }
  8055.  
  8056. //----------------------------------------------------------------------------
  8057. // CEditImageElement
  8058. //----------------------------------------------------------------------------
  8059.  
  8060. //
  8061. // LTNOTE: these should be static functions on CEditImageData..
  8062. //
  8063. EDT_ImageData* edt_NewImageData(){
  8064.     EDT_ImageData *pData = XP_NEW( EDT_ImageData );
  8065.     if( pData == 0 ){
  8066.         // throw();
  8067.         return pData;
  8068.     }
  8069.     pData->bIsMap = FALSE;
  8070. //    pData->pUseMap = 0;  now in pExtra
  8071.     pData->align = ED_ALIGN_DEFAULT;
  8072.     pData->pSrc = 0;
  8073.     pData->pLowSrc = 0;
  8074.     pData->pName = 0;
  8075.     pData->pAlt = 0;
  8076.     pData->iWidth = 0;
  8077.     pData->iHeight = 0;
  8078.     pData->iOriginalWidth = 0;
  8079.     pData->iOriginalHeight = 0;
  8080.     pData->bWidthPercent = FALSE;
  8081.     pData->bHeightPercent = FALSE;
  8082.     pData->iHSpace = 0;
  8083.     pData->iVSpace = 0;
  8084.     pData->iBorder = -1;
  8085.     pData->bNoSave = FALSE;
  8086.     pData->pHREFData = NULL;
  8087.     pData->pExtra = 0;
  8088.     return pData;
  8089. }
  8090.  
  8091. EDT_ImageData* edt_DupImageData( EDT_ImageData *pOldData ){
  8092.     EDT_ImageData *pData = XP_NEW( EDT_ImageData );
  8093.  
  8094. //    pData->pUseMap = edt_StrDup(pOldData->pUseMap);  now in pExtra
  8095.     pData->pSrc = edt_StrDup(pOldData->pSrc);
  8096.     pData->pName = edt_StrDup(pOldData->pName);
  8097.     pData->pLowSrc = edt_StrDup(pOldData->pLowSrc);
  8098.     pData->pAlt = edt_StrDup(pOldData->pAlt);
  8099.     pData->pExtra = edt_StrDup(pOldData->pExtra);
  8100.  
  8101.     pData->bIsMap = pOldData->bIsMap;
  8102.     pData->align = pOldData->align;
  8103.     pData->iWidth = pOldData->iWidth;
  8104.     pData->iHeight = pOldData->iHeight;
  8105.     pData->iOriginalWidth = pOldData->iOriginalWidth;
  8106.     pData->iOriginalHeight = pOldData->iOriginalHeight;
  8107.     pData->bWidthPercent = pOldData->bWidthPercent;
  8108.     pData->bHeightPercent = pOldData->bHeightPercent;
  8109.     pData->iHSpace = pOldData->iHSpace;
  8110.     pData->iVSpace = pOldData->iVSpace;
  8111.     pData->iBorder = pOldData->iBorder;
  8112.     pData->bNoSave = pOldData->bNoSave;
  8113.  
  8114.     if( pOldData->pHREFData ){
  8115.         pData->pHREFData = EDT_DupHREFData( pOldData->pHREFData );
  8116.     }
  8117.     else {
  8118.         pData->pHREFData = 0;
  8119.     }
  8120.         
  8121.     return pData;
  8122. }
  8123.  
  8124.  
  8125. void edt_FreeImageData( EDT_ImageData *pData ){
  8126. //    if( pData->pUseMap ) XP_FREE( pData->pUseMap );  now in pExtra
  8127.     if( pData->pSrc ) XP_FREE( pData->pSrc );
  8128.     if( pData->pLowSrc ) XP_FREE( pData->pLowSrc );
  8129.     if( pData->pName ) XP_FREE( pData->pName );
  8130.     if( pData->pAlt ) XP_FREE( pData->pAlt );
  8131.     if( pData->pExtra ) XP_FREE( pData->pExtra );
  8132.     if( pData->pHREFData ) EDT_FreeHREFData( pData->pHREFData );
  8133.     XP_FREE( pData );
  8134. }
  8135.  
  8136. //
  8137. // Constructors, Streamers..
  8138. //
  8139. CEditImageElement::CEditImageElement( CEditElement *pParent, PA_Tag* pTag, int16 csid,
  8140.                 ED_LinkId href ):
  8141.             CEditLeafElement( pParent, P_IMAGE ), 
  8142.             m_pLoImage(0),
  8143.             m_pParams(0),
  8144.             m_iHeight(0),
  8145.             m_iWidth(0),
  8146.             m_bWidthPercent(0),
  8147.             m_bHeightPercent(0),
  8148.             m_iSaveIndex(0),
  8149.             m_iSaveLowIndex(0),
  8150.             m_href(ED_LINK_ID_NONE),
  8151.             m_align( ED_ALIGN_DEFAULT),
  8152.             m_bSizeWasGiven(FALSE),
  8153.             m_bSizeIsBogus(FALSE)
  8154. {
  8155.     // must set href before calling SetImageData to allow GetDefaultBorder to work
  8156.     if( href != ED_LINK_ID_NONE ){
  8157.         SetHREF( href );
  8158.     }
  8159.     if( pTag ){
  8160.         EDT_ImageData *pData = ParseParams( pTag, csid );
  8161.         SetImageData( pData );
  8162.         edt_FreeImageData( pData );
  8163.     }
  8164. }
  8165.  
  8166. CEditImageElement::CEditImageElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer):
  8167.             CEditLeafElement(pStreamIn, pBuffer), 
  8168.             m_pLoImage(0),
  8169.             m_pParams(0), 
  8170.             m_href(ED_LINK_ID_NONE),
  8171.             m_align(ED_ALIGN_DEFAULT) {
  8172.     m_align = (ED_Alignment) pStreamIn->ReadInt();
  8173.     m_bSizeWasGiven = (XP_Bool) pStreamIn->ReadInt();
  8174.     m_bSizeIsBogus = (XP_Bool) pStreamIn->ReadInt();
  8175.     m_iHeight = pStreamIn->ReadInt();
  8176.     m_iWidth = pStreamIn->ReadInt();
  8177.     m_bWidthPercent =  pStreamIn->ReadInt();
  8178.     m_bHeightPercent =  pStreamIn->ReadInt();
  8179.     m_pParams = pStreamIn->ReadZString();
  8180.     char *pHrefString = pStreamIn->ReadZString();
  8181.     char *pExtra = pStreamIn->ReadZString();
  8182.     if( pHrefString ){
  8183.         SetHREF( pBuffer->linkManager.Add( pHrefString, pExtra ));
  8184.         XP_FREE( pHrefString );
  8185.         if( pExtra ) XP_FREE( pExtra );
  8186.     }
  8187. }
  8188.  
  8189. CEditImageElement::~CEditImageElement(){
  8190.     DisconnectLayoutElements((LO_Element*) m_pLoImage);
  8191.     if( m_pParams) XP_FREE( m_pParams ); 
  8192.     if( m_href != ED_LINK_ID_NONE ) m_href->Release();
  8193. }
  8194.  
  8195. // Return TRUE if pURL has changed.
  8196. PRIVATE XP_Bool edt_make_image_relative(char *pBase,char **pURL) {
  8197.   if (!pBase || !*pBase || !pURL || !*pURL || !**pURL) {
  8198.     // Base and pURL must be existing non-empty strings.
  8199.     return FALSE;
  8200.   }
  8201.  
  8202.   if ((*pURL)[0] == '/') {
  8203.     // Don't change URL if using absolute pathing.
  8204.     return FALSE;
  8205.   }
  8206.  
  8207.   char *pAbs = NET_MakeAbsoluteURL(pBase,*pURL); // In case pURL was already relative.
  8208.   char *pRel;
  8209.   NET_MakeRelativeURL(pBase,pAbs,&pRel);
  8210.   XP_FREEIF(pAbs);
  8211.  
  8212.   if (pRel && XP_STRCMP(pRel,*pURL)) {
  8213.     // pRel exists and is different than original value.
  8214.     XP_FREEIF(*pURL);
  8215.     *pURL = pRel;
  8216.     return TRUE;
  8217.   }
  8218.   else {
  8219.     XP_FREEIF(pRel);
  8220.     return FALSE;
  8221.   }
  8222. }
  8223.  
  8224. void CEditImageElement::SetLayoutElement( intn iEditOffset, intn lo_type, 
  8225.                         LO_Element* pLoElement ){
  8226.     SetLayoutElementHelper(LO_IMAGE, (LO_Element**) &m_pLoImage,
  8227.         iEditOffset, lo_type, pLoElement);
  8228.     // Zero out the image_req field. We "know" that it is garbage at this point.
  8229.     pLoElement->lo_image.image_req = 0;
  8230.  
  8231.     // Make image URLs relative to document. bug 41009.
  8232.     EDT_ImageData *pData = GetImageData();
  8233.     CEditBuffer *pBuffer = GetEditBuffer();
  8234.     if( pData ) {
  8235.       XP_Bool bChanged = FALSE;
  8236.       if (pBuffer && !EDT_IS_NEW_DOCUMENT(pBuffer->m_pContext)){
  8237.         if (edt_make_image_relative(pBuffer->GetBaseURL(),&pData->pSrc)) {
  8238.           bChanged = TRUE;
  8239.         }
  8240.         if (edt_make_image_relative(pBuffer->GetBaseURL(),&pData->pLowSrc)) {
  8241.           bChanged = TRUE;
  8242.         }
  8243.       }
  8244.  
  8245.       if (bChanged) {
  8246.         SetImageData(pData);
  8247.       }
  8248.       edt_FreeImageData(pData);
  8249.     }
  8250. }
  8251.  
  8252. void CEditImageElement::ResetLayoutElement( intn iEditOffset, 
  8253.             LO_Element* pLoElement ){
  8254.     ResetLayoutElementHelper((LO_Element**) &m_pLoImage,
  8255.         iEditOffset, pLoElement);
  8256. }
  8257.  
  8258. LO_Element* CEditImageElement::GetLayoutElement(){
  8259.     return (LO_Element*)m_pLoImage;
  8260. }
  8261.  
  8262. void CEditImageElement::StreamOut( IStreamOut *pOut){
  8263.     CEditLeafElement::StreamOut( pOut );
  8264.     
  8265.     pOut->WriteInt( (int32)m_align );
  8266.     pOut->WriteInt( m_bSizeWasGiven );
  8267.     pOut->WriteInt( m_bSizeIsBogus );
  8268.     pOut->WriteInt( m_iHeight );
  8269.     pOut->WriteInt( m_iWidth );
  8270.     pOut->WriteInt( m_bWidthPercent );
  8271.     pOut->WriteInt( m_bHeightPercent );
  8272.  
  8273.     // We need to make the Image Src and LowSrc Parameters absolute.
  8274.     // We do this by first converting the parameter string to and Edit
  8275.     //  data structure and then making the paths absolute.
  8276.     EDT_ImageData *pData = GetImageData();
  8277.     CEditBuffer *pBuffer = GetEditBuffer();
  8278.     if( pData && pBuffer && pBuffer->GetBaseURL() ){
  8279.         char *p;
  8280.         if( pData->pSrc ) {
  8281.             p = NET_MakeAbsoluteURL( pBuffer->GetBaseURL(), pData->pSrc );
  8282.             if( p ){
  8283.                 XP_FREE( pData->pSrc );
  8284.                 pData->pSrc = p;
  8285.             }
  8286.         }
  8287.         if( pData->pLowSrc ) {
  8288.             p = NET_MakeAbsoluteURL( pBuffer->GetBaseURL(), pData->pLowSrc );
  8289.             if( p ){
  8290.                 XP_FREE( pData->pLowSrc );
  8291.                 pData->pLowSrc = p;
  8292.             }
  8293.         }
  8294.  
  8295.         // Format the parameters for output.
  8296.         p = FormatParams( pData, TRUE );
  8297.         pOut->WriteZString( p );
  8298.         XP_FREE(p);
  8299.         edt_FreeImageData( pData );
  8300.     }
  8301.     else {
  8302.         pOut->WriteZString( m_pParams );
  8303.     }
  8304.  
  8305.  
  8306.     if( m_href != ED_LINK_ID_NONE ){
  8307.         pOut->WriteZString( m_href->hrefStr );
  8308.         pOut->WriteZString( m_href->pExtra );
  8309.     }
  8310.     else {
  8311.         pOut->WriteZString( 0 );
  8312.         pOut->WriteZString( 0 );
  8313.     }
  8314. }
  8315.  
  8316. EEditElementType CEditImageElement::GetElementType()
  8317. {
  8318.     return eImageElement;
  8319. }
  8320.  
  8321. PA_Tag* CEditImageElement::TagOpen( int /* iEditOffset */ ){
  8322.     char *pCur = 0;
  8323.     PA_Tag *pRetTag = 0;
  8324.     PA_Tag *pEndTag = 0;
  8325.  
  8326.     if( m_href != ED_LINK_ID_NONE ){
  8327.         char *pBuf = PR_smprintf("HREF=%s %s>", edt_MakeParamString(m_href->hrefStr),
  8328.             (m_href->pExtra ? m_href->pExtra : "") );        
  8329.         edt_AddTag( pRetTag, pEndTag, P_ANCHOR, FALSE, pBuf );
  8330.         free( pBuf );
  8331.     }
  8332.  
  8333.     if( m_pParams ){
  8334.         if( SizeIsKnown() ){    
  8335.             // Append the HEIGHT and WIDTH params, taking into account "%" mode
  8336.             pCur = XP_STRDUP(m_pParams);
  8337.  
  8338.             if( m_bHeightPercent ){
  8339.                 pCur = PR_sprintf_append( pCur, " HEIGHT=\"%ld%%\" ", 
  8340.                                           (long)min(100,max(1,m_iHeight)) );
  8341.             }
  8342.             else {
  8343.                 pCur = PR_sprintf_append( pCur, " HEIGHT=\"%ld\" ", (long)m_iHeight );
  8344.             }
  8345.  
  8346.             if( m_bWidthPercent ){
  8347.                 pCur = PR_sprintf_append( pCur, " WIDTH=\"%ld%%\" ", 
  8348.                                           (long)min(100,max(1,m_iWidth)) );
  8349.             }
  8350.             else {
  8351.                 pCur = PR_sprintf_append( pCur, " WIDTH=\"%ld\" ", (long)m_iWidth );
  8352.             }
  8353.             //CLM: Not sure if this is best behavior, but if size is
  8354.             //     already set in HTML, should consider that to be "original"?
  8355.             //     Maybe we should fill in here if we will end up getting same values in FinishedLoad
  8356.             //m_iOriginalHeight = m_iHeight;
  8357.             //m_iOriginalWidth = m_iWidth;
  8358.         }
  8359.         else {
  8360.             // We don't know the size, but if we don't give a size,
  8361.             // and if the image isn't known, we will block and
  8362.             // crash. So we put in a bogus size here, and take it
  8363.             // out in FinishedLoad if there's image data available.
  8364.             m_bSizeIsBogus = TRUE;
  8365.             pCur = PR_smprintf("%s HEIGHT=24 WIDTH=22", m_pParams );
  8366.         }
  8367.     }        
  8368.  
  8369.     // For editing, we don't support baseline, left or right alignment of images.
  8370.  
  8371.     if( m_align != ED_ALIGN_DEFAULT && m_align != ED_ALIGN_BASELINE 
  8372.                 && m_align != ED_ALIGN_LEFT && m_align != ED_ALIGN_RIGHT ){
  8373.         pCur = PR_sprintf_append(pCur, "ALIGN=%s ", EDT_AlignString(m_align) );
  8374.     }
  8375.     
  8376.     pCur = PR_sprintf_append(pCur, ">");
  8377.  
  8378.     edt_AddTag( pRetTag, pEndTag, P_IMAGE, FALSE, pCur );
  8379.     pEndTag->edit_element = this;
  8380.  
  8381.     if( m_href != ED_LINK_ID_NONE ){
  8382.         edt_AddTag( pRetTag, pEndTag, P_ANCHOR, TRUE );
  8383.     }
  8384.     free( pCur );
  8385.     return pRetTag;
  8386. }
  8387.  
  8388. void CEditImageElement::PrintOpen( CPrintState *pPrintState ){
  8389.     if( m_href != ED_LINK_ID_NONE ){
  8390.         char *pS1 = "";
  8391.         char *pS2 = "";
  8392.         if( m_href->pExtra ){
  8393.             pS1 = " ";
  8394.             pS2 = m_href->pExtra;
  8395.         }
  8396.         pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( 
  8397.             "<A HREF=%s%s%s>", edt_MakeParamString(m_href->hrefStr), pS1, pS2 );
  8398.     }
  8399.  
  8400.     // Use special versions of params which has BORDER removed if it's the default
  8401.  
  8402.     char* pParams = NULL;
  8403.     {
  8404.         EDT_ImageData* pData = GetImageData();
  8405.         pParams = FormatParams(pData, TRUE);
  8406.         EDT_FreeImageData(pData);
  8407.     }
  8408.  
  8409.     if( SizeIsKnown() && ! m_bSizeIsBogus ){
  8410.         pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "<IMG %s", (pParams ? pParams : " ") );
  8411.  
  8412.         if( m_bHeightPercent ){
  8413.             pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "HEIGHT=%ld%%", (long)m_iHeight );
  8414.         } else {
  8415.             pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "HEIGHT=%ld", (long)m_iHeight );
  8416.         }
  8417.         if( m_bWidthPercent ){
  8418.             pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( " WIDTH=%ld%%", (long)m_iWidth );
  8419.         } else {
  8420.             pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( " WIDTH=%ld", (long)m_iWidth );
  8421.         }
  8422.     }
  8423.     else {
  8424.         pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "<IMG %s", 
  8425.                     (pParams ? pParams : " " ));
  8426.     }
  8427.  
  8428.     if ( pParams )
  8429.         XP_FREE(pParams);
  8430.  
  8431.     if( m_align != ED_ALIGN_DEFAULT && m_align != ED_ALIGN_BASELINE ){
  8432.         pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( " ALIGN=%s", 
  8433.                 EDT_AlignString(m_align));
  8434.     }
  8435.  
  8436.     pPrintState->m_pOut->Write( ">", 1 );
  8437.     pPrintState->m_iCharPos++;
  8438.     
  8439.     if( m_href != ED_LINK_ID_NONE ){
  8440.         pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( 
  8441.             "</A>" );        
  8442.     }
  8443.  
  8444. }
  8445.  
  8446. static char *imageParams[] = {
  8447.     PARAM_ISMAP,
  8448. //    PARAM_USEMAP,     Now in extra
  8449.     PARAM_SRC,
  8450.     PARAM_LOWSRC,
  8451.     PARAM_NAME,
  8452.     PARAM_ALT,
  8453.     PARAM_WIDTH,
  8454.     PARAM_HEIGHT,
  8455.     PARAM_ALIGN,
  8456.     PARAM_HSPACE,
  8457.     PARAM_VSPACE,
  8458.     PARAM_BORDER,
  8459.     PARAM_ALIGN,
  8460.     PARAM_NOSAVE,
  8461.     0
  8462. };
  8463.  
  8464. EDT_ImageData* CEditImageElement::ParseParams( PA_Tag *pTag, int16 csid ){
  8465.     PA_Block buff;
  8466.     EDT_ImageData* pData = edt_NewImageData();
  8467.  
  8468.     buff = PA_FetchParamValue(pTag, PARAM_ISMAP, csid);
  8469.     if (buff != NULL){
  8470.         pData->bIsMap = TRUE;
  8471.         PA_FREE(buff);
  8472.     }
  8473.     buff = PA_FetchParamValue(pTag, PARAM_NOSAVE, csid);
  8474.     if (buff != NULL){
  8475.         pData->bNoSave = TRUE;
  8476.         PA_FREE(buff);
  8477.     }
  8478. //    pData->pUseMap = edt_FetchParamString( pTag, PARAM_USEMAP, csid ); now in pExtra
  8479.     pData->pSrc = edt_FetchParamString( pTag, PARAM_SRC, csid );
  8480.     pData->pLowSrc = edt_FetchParamString( pTag, PARAM_LOWSRC, csid );
  8481.     pData->pName = edt_FetchParamString( pTag, PARAM_NAME, csid );
  8482.     pData->pAlt = edt_FetchParamString( pTag, PARAM_ALT, csid );
  8483.     
  8484.     // Get width dimension from string and parse for "%" Default = 100%
  8485.     edt_FetchDimension( pTag, PARAM_WIDTH, 
  8486.                         &pData->iWidth, &pData->bWidthPercent,
  8487.                         0, FALSE, csid );
  8488.     edt_FetchDimension( pTag, PARAM_HEIGHT, 
  8489.                         &pData->iHeight, &pData->bHeightPercent,
  8490.                         0, FALSE, csid );
  8491.     m_bSizeWasGiven = pData->iWidth != 0 && pData->iHeight != 0;
  8492.  
  8493.     pData->align = edt_FetchParamAlignment( pTag, ED_ALIGN_BASELINE, FALSE, csid );
  8494.  
  8495.     pData->iHSpace = edt_FetchParamInt( pTag, PARAM_HSPACE, 0, csid );
  8496.     pData->iVSpace = edt_FetchParamInt( pTag, PARAM_VSPACE, 0, csid );
  8497.     pData->iBorder = edt_FetchParamInt( pTag, PARAM_BORDER, -1, csid );
  8498.     pData->pHREFData = 0;
  8499.  
  8500.     pData->pExtra = edt_FetchParamExtras( pTag, imageParams, csid );
  8501.  
  8502.     return pData;
  8503. }
  8504.  
  8505. EDT_ImageData* CEditImageElement::GetImageData(){
  8506.     EDT_ImageData *pRet;
  8507.  
  8508.     // LTNOTE: hackola time.  We don't want TagOpen to return multiple tags
  8509.     //  in this use, so we prevent it by faking out the href ID and restoring
  8510.     //  it when we are done.  Not real maintainable.
  8511.  
  8512.     ED_LinkId saveHref = m_href;
  8513.     m_href = ED_LINK_ID_NONE;
  8514.     PA_Tag* pTag = TagOpen(0);
  8515.     m_href = saveHref;
  8516.  
  8517.     pRet = ParseParams( pTag, GetWinCSID() );
  8518.  
  8519.     if ( m_bSizeIsBogus ) {
  8520.         pRet->iWidth = 0; pRet->bWidthPercent = FALSE;
  8521.         pRet->iHeight = 0; pRet->bHeightPercent = FALSE;
  8522.     }
  8523.  
  8524.     //CLM: We stored this when image was first loaded:
  8525.     int originalWidth = 0;
  8526.     int originalHeight = 0;
  8527.     if ( m_pLoImage ) {
  8528.         IL_GetNaturalDimensions( m_pLoImage->image_req, &originalWidth, &originalHeight);
  8529.     }
  8530.     pRet->iOriginalWidth = originalWidth;
  8531.     pRet->iOriginalHeight = originalHeight;
  8532.     
  8533.     pRet->bWidthPercent = m_bWidthPercent;
  8534.     pRet->bHeightPercent = m_bHeightPercent;
  8535.     
  8536.     if( m_href != ED_LINK_ID_NONE ){
  8537.         pRet->pHREFData = m_href->GetData();
  8538.     }
  8539.     pRet->align = m_align;
  8540.     PA_FreeTag( pTag );
  8541.     return pRet;
  8542. }
  8543.  
  8544. // border is weird.  If we are in a link the border is default 2, if 
  8545. //  we are not in a link, border is default 0.  In order not to foist this
  8546. //  weirdness on the user, we are always explicit on our image borders.
  8547. //
  8548. int32 CEditImageElement::GetDefaultBorder(){
  8549.     return ( m_href != ED_LINK_ID_NONE ) ? 2 : 0;
  8550. }
  8551.  
  8552. XP_Bool CEditImageElement::GetLOElementAndOffset( ElementOffset iEditOffset, XP_Bool /*bEditStickyAfter*/ ,
  8553.             LO_Element*& pRetElement,
  8554.             int& pLayoutOffset ){
  8555.     pLayoutOffset = 0;
  8556.     pRetElement = (LO_Element*)m_pLoImage;
  8557.     pLayoutOffset = (int) iEditOffset;
  8558.     return TRUE;
  8559. }
  8560.  
  8561. void CEditImageElement::SetImageData( EDT_ImageData *pData ){
  8562.     char* pCur = FormatParams(pData, FALSE);
  8563.  
  8564.     if( m_pParams ){
  8565.         XP_FREE( m_pParams );
  8566.     }
  8567.     m_pParams = pCur;
  8568.  
  8569.     m_align = pData->align;
  8570.     
  8571.     if (pData->iHeight != 0 && pData->iWidth != 0 ) {
  8572.         m_bSizeWasGiven = TRUE;
  8573.         m_bSizeIsBogus = FALSE;
  8574.         m_bHeightPercent = pData->bHeightPercent;
  8575.         m_bWidthPercent = pData->bWidthPercent;
  8576.     }
  8577.     else {
  8578.         m_bSizeWasGiven = FALSE;
  8579.         //CLM: We were given Width or Height = 0
  8580.         //     so we will get REAL dimensions
  8581.         // Clearing these willl allow us to retrieve actual dimensions
  8582.         //  on an image that had WIDTH and/or HEIGHT values on 1st load
  8583.         m_bHeightPercent = 0;
  8584.         m_bWidthPercent = 0;
  8585.     }
  8586.     m_iHeight = pData->iHeight;
  8587.     m_iWidth = pData->iWidth;
  8588. }
  8589.  
  8590. char* CEditImageElement::FormatParams(EDT_ImageData* pData, XP_Bool bForPrinting){
  8591.     char *pCur = 0;
  8592.  
  8593.     if( pData->bIsMap ){ 
  8594.         pCur = PR_sprintf_append( pCur, "ISMAP " );
  8595.     }
  8596.  
  8597. /*  USEMAP now in pExtra
  8598.     if( pData->pUseMap ){
  8599.         pCur = PR_sprintf_append( pCur, "USEMAP=%s ", edt_MakeParamString(pData->pUseMap) );
  8600.     }
  8601. */
  8602.  
  8603.     if( pData->pSrc ){
  8604.         pCur = PR_sprintf_append( pCur, "SRC=%s ", edt_MakeParamString( pData->pSrc ) );
  8605.     }
  8606.  
  8607.     if( pData->pLowSrc ){
  8608.         pCur = PR_sprintf_append( pCur, "LOWSRC=%s ", edt_MakeParamString( pData->pLowSrc ) );
  8609.     }
  8610.  
  8611.     if( pData->pName ){
  8612.         pCur = PR_sprintf_append( pCur, "NAME=%s ", edt_MakeParamString( pData->pName ) );
  8613.     }
  8614.  
  8615.     if( pData->pAlt ){
  8616.         pCur = PR_sprintf_append( pCur, "ALT=%s ", edt_MakeParamString( pData->pAlt ) );
  8617.     }
  8618.  
  8619.     if( pData->iHSpace ){
  8620.         pCur = PR_sprintf_append( pCur, "HSPACE=%ld ", (long)pData->iHSpace );
  8621.     }
  8622.  
  8623.     if( pData->iVSpace ){
  8624.         pCur = PR_sprintf_append( pCur, "VSPACE=%ld ", (long)pData->iVSpace );
  8625.     }
  8626.  
  8627.     if( pData->bNoSave ){ 
  8628.         pCur = PR_sprintf_append( pCur, "NOSAVE " );
  8629.     }
  8630.  
  8631.     // border is weird.  If we are in a link the border is default 2, if 
  8632.     // we are not in a link, border is default 0.  When printing, we only write out a
  8633.     // border if it's different than the default.
  8634.     //
  8635.     // Other times, we always write it out because we want it to stay the same even if the
  8636.     // default border size changes. Whew!
  8637.     {
  8638.         if( !bForPrinting || ( pData->iBorder >=0 )){
  8639.             pCur = PR_sprintf_append( pCur, "BORDER=%ld ", (long)pData->iBorder );
  8640.         }
  8641.     }
  8642.  
  8643.     if( pData->pExtra  != 0 ){
  8644.         pCur = PR_sprintf_append( pCur, "%s ", pData->pExtra);
  8645.     }
  8646.     return pCur;
  8647. }
  8648.  
  8649. void CEditImageElement::FinishedLoad( CEditBuffer *pBuffer ){
  8650.     if( m_pLoImage ){
  8651.         if ( ! m_bSizeWasGiven || m_bSizeIsBogus ) {
  8652.             // The user didn't specify a size. Look at what we got.
  8653.             m_iHeight = pBuffer->m_pContext->convertPixY * m_pLoImage->height;
  8654.             m_iWidth = pBuffer->m_pContext->convertPixX * m_pLoImage->width;
  8655.         }
  8656.     }
  8657. }
  8658.  
  8659. void CEditImageElement::SetHREF( ED_LinkId id ){
  8660.     if( m_href != ED_LINK_ID_NONE ){
  8661.         m_href->Release();
  8662.     }
  8663.     m_href = id;
  8664.     if( id != ED_LINK_ID_NONE ){
  8665.         id->AddRef();
  8666.     }
  8667. }
  8668.  
  8669. XP_Bool CEditImageElement::SizeIsKnown() {
  8670.     return m_iHeight > 0;
  8671. }
  8672.  
  8673. void CEditImageElement::MaskData( EDT_CharacterData*& pData ){
  8674.     if( pData == 0 ){
  8675.         pData = GetCharacterData();
  8676.         return;
  8677.     }
  8678.     
  8679.     // An image can have an HREF text format
  8680.     ED_TextFormat tf = ( m_href == ED_LINK_ID_NONE ) ? 0 : TF_HREF;
  8681.  
  8682.     ED_TextFormat differences = pData->values ^ tf;
  8683.     if( differences ){
  8684.         pData->mask &= ~differences;
  8685.     }
  8686.     if( (pData->mask & pData->values & tf & TF_HREF)
  8687.                 && (pData->linkId != m_href ) ){
  8688.         pData->mask &= ~TF_HREF;
  8689.     }
  8690. }
  8691.  
  8692. EDT_CharacterData* CEditImageElement::GetCharacterData(){
  8693.     EDT_CharacterData *pData = EDT_NewCharacterData();
  8694.     // We are only interested in HREF?
  8695.     // pData->mask = TF_HREF;
  8696.     pData->mask = -1;
  8697.     if( m_href != ED_LINK_ID_NONE ){
  8698.         pData->pHREFData = m_href->GetData();
  8699.         pData->values = TF_HREF;
  8700.     }
  8701.     pData->linkId = m_href;
  8702.     return pData;
  8703. }
  8704. //-----------------------------------------------------------------------------
  8705. // CEditHorizRuleElement
  8706. //-----------------------------------------------------------------------------
  8707.  
  8708.  
  8709. CEditHorizRuleElement::CEditHorizRuleElement( CEditElement *pParent, PA_Tag* pTag, int16 /*csid*/ ):
  8710.             CEditLeafElement( pParent, P_HRULE ), 
  8711.             m_pLoHorizRule(0)
  8712.             {
  8713.  
  8714.     if( pTag ){
  8715.         char *locked_buff;
  8716.                 
  8717.         PA_LOCK(locked_buff, char *, pTag->data );
  8718.         if( locked_buff ){
  8719.             SetTagData( locked_buff );
  8720.         }
  8721.         PA_UNLOCK(pTag->data);
  8722.     }
  8723. }
  8724.  
  8725. CEditHorizRuleElement::CEditHorizRuleElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer):
  8726.             CEditLeafElement(pStreamIn, pBuffer), 
  8727.             m_pLoHorizRule(0)
  8728.             {
  8729. }
  8730.  
  8731. CEditHorizRuleElement::~CEditHorizRuleElement(){
  8732.     DisconnectLayoutElements((LO_Element*) m_pLoHorizRule);
  8733. }
  8734.  
  8735.  
  8736. LO_Element* CEditHorizRuleElement::GetLayoutElement(){
  8737.     return (LO_Element*)m_pLoHorizRule;
  8738. }
  8739.  
  8740. void CEditHorizRuleElement::SetLayoutElement( intn iEditOffset, intn lo_type, 
  8741.                         LO_Element* pLoElement ){
  8742.     SetLayoutElementHelper(LO_HRULE, (LO_Element**) &m_pLoHorizRule,
  8743.         iEditOffset, lo_type, pLoElement);
  8744. }
  8745.  
  8746. void CEditHorizRuleElement::ResetLayoutElement( intn iEditOffset, 
  8747.             LO_Element* pLoElement ){
  8748.     ResetLayoutElementHelper((LO_Element**) &m_pLoHorizRule,
  8749.         iEditOffset, pLoElement);
  8750. }
  8751.  
  8752. void CEditHorizRuleElement::StreamOut( IStreamOut *pOut ){
  8753.     CEditLeafElement::StreamOut( pOut );
  8754. }
  8755.  
  8756. EEditElementType CEditHorizRuleElement::GetElementType()
  8757. {
  8758.     return eHorizRuleElement;
  8759. }
  8760.  
  8761. XP_Bool CEditHorizRuleElement::GetLOElementAndOffset( ElementOffset iEditOffset, XP_Bool /* bEditStickyAfter */,
  8762.             LO_Element*& pRetElement,
  8763.             int& pLayoutOffset ){
  8764.     pRetElement = (LO_Element*)m_pLoHorizRule;
  8765.     pLayoutOffset = (int) iEditOffset;
  8766.     return TRUE;
  8767. }
  8768.  
  8769. // Property Getting and Setting Stuff.
  8770.  
  8771. EDT_HorizRuleData* CEditHorizRuleElement::NewData(){
  8772.     EDT_HorizRuleData *pData = XP_NEW( EDT_HorizRuleData );
  8773.     if( pData == 0 ){
  8774.         // throw();
  8775.         return pData;
  8776.     }
  8777.     pData->align = ED_ALIGN_CENTER;
  8778.     pData->size = DEFAULT_HR_THICKNESS;
  8779.     pData->bNoShade = 0;
  8780.     pData->iWidth = 100;
  8781.     pData->bWidthPercent = TRUE;
  8782.     pData->pExtra = 0;
  8783.     return pData;
  8784. }
  8785.  
  8786. void CEditHorizRuleElement::FreeData( EDT_HorizRuleData *pData ){
  8787.     if( pData->pExtra ) XP_FREE( pData->pExtra );
  8788.     XP_FREE( pData );
  8789. }
  8790.  
  8791. void CEditHorizRuleElement::SetData( EDT_HorizRuleData *pData ){
  8792.     char *pNew = 0;
  8793.  
  8794.     if( pData->align == ED_ALIGN_RIGHT || pData->align == ED_ALIGN_LEFT ){
  8795.         pNew = PR_sprintf_append( pNew, "ALIGN=%s ", EDT_AlignString(pData->align) );
  8796.     }
  8797.     if( pData->size != DEFAULT_HR_THICKNESS ){
  8798.         pNew = PR_sprintf_append( pNew, "SIZE=%ld ", (long)pData->size );
  8799.     }
  8800.     if( pData->bNoShade ){
  8801.         pNew = PR_sprintf_append( pNew, "NOSHADE " );
  8802.     }
  8803.     if( pData->iWidth ){
  8804.         if( pData->bWidthPercent ){
  8805.             pNew = PR_sprintf_append( pNew, "WIDTH=\"%ld%%\" ", 
  8806.                                       (long)min(100,max(1,pData->iWidth)) );
  8807.         }
  8808.         else {
  8809.             pNew = PR_sprintf_append( pNew, "WIDTH=\"%ld\" ", (long)pData->iWidth );
  8810.         }
  8811.     }
  8812.     if( pData->pExtra){
  8813.         pNew = PR_sprintf_append( pNew, "%s ", pData->pExtra );
  8814.     }
  8815.  
  8816.     pNew = PR_sprintf_append( pNew, ">" );
  8817.  
  8818.     SetTagData( pNew );        
  8819.     free(pNew);
  8820. }
  8821.  
  8822. EDT_HorizRuleData* CEditHorizRuleElement::GetData( ){
  8823.     EDT_HorizRuleData *pRet;
  8824.     PA_Tag* pTag = TagOpen(0);
  8825.     pRet = ParseParams( pTag, GetWinCSID() );
  8826.     PA_FreeTag( pTag );
  8827.     return pRet;
  8828. }
  8829.  
  8830. static char *hruleParams[] = {
  8831.     PARAM_ALIGN,
  8832.     PARAM_NOSHADE,
  8833.     PARAM_WIDTH,
  8834.     PARAM_SIZE,
  8835.     0
  8836. };
  8837.  
  8838. EDT_HorizRuleData* CEditHorizRuleElement::ParseParams( PA_Tag *pTag, int16 csid ){
  8839.     EDT_HorizRuleData *pData = NewData();
  8840.     ED_Alignment align;
  8841.     
  8842.     align = edt_FetchParamAlignment( pTag, ED_ALIGN_CENTER, FALSE, csid );
  8843.     if( align == ED_ALIGN_RIGHT || align == ED_ALIGN_LEFT ){
  8844.         pData->align = align;
  8845.     }
  8846.     // Get width dimension from string and parse for "%" Default = 100%
  8847.     edt_FetchDimension( pTag, PARAM_WIDTH, 
  8848.                         &pData->iWidth, &pData->bWidthPercent,
  8849.                         100, TRUE, csid );
  8850.     pData->bNoShade = edt_FetchParamBoolExist( pTag, PARAM_NOSHADE, csid );
  8851.     pData->size = edt_FetchParamInt( pTag, PARAM_SIZE, DEFAULT_HR_THICKNESS, csid );
  8852.     pData->pExtra = edt_FetchParamExtras( pTag, hruleParams, csid );
  8853.     return pData;
  8854. }
  8855.  
  8856. //-----------------------------------------------------------------------------
  8857. // CEditIconElement
  8858. //-----------------------------------------------------------------------------
  8859. static char *ppIconTags[] = {
  8860.     "align=absbotom border=0 src=\"internal-edit-named-anchor\" alt=\"%s\">",
  8861.     "align=absbotom border=0 src=\"internal-edit-form-element\">",
  8862.     "src=\"internal-edit-unsupported-tag\" alt=\"<%s\"",
  8863.     "src=\"internal-edit-unsupported-end-tag\" alt=\"</%s\"",
  8864.     "align=absbotom border=0 src=\"internal-edit-java\">",
  8865.     "align=absbotom border=0 src=\"internal-edit-plugin\">",
  8866.     0
  8867. };
  8868.  
  8869. CEditIconElement::CEditIconElement( CEditElement *pParent, int32 iconTag, 
  8870.                         PA_Tag* pTag, int16 /*csid*/ )
  8871.             :
  8872.                 CEditLeafElement( pParent, P_IMAGE ), 
  8873.                 m_originalTagType( pTag ? pTag->type : P_UNKNOWN ),
  8874.                 m_iconTag( iconTag ),
  8875.                 m_bEndTag( pTag ? pTag->is_end : 0 ),
  8876.                 m_pSpoofData(0),
  8877.                 m_pLoIcon(0),
  8878.                 m_piSaveIndices(NULL)
  8879.             {
  8880.  
  8881.     if( pTag ){
  8882.         char *locked_buff;
  8883.                 
  8884.         PA_LOCK(locked_buff, char *, pTag->data );
  8885.         if( locked_buff ){
  8886.             char *p = locked_buff;
  8887.             if( m_originalTagType != P_ANCHOR 
  8888.                     && m_originalTagType != P_UNKNOWN ){
  8889.  
  8890.                 while( *p == ' ' ) p++;
  8891.                 char *pSpace = " ";
  8892.                 if( *p == '>' ) pSpace = "";
  8893.  
  8894.                 char *pTagData = PR_smprintf("%s%s%s", 
  8895.                         EDT_TagString(m_originalTagType), pSpace, p );
  8896.                 SetTagData( pTagData );
  8897.                 SetTagData( pTag, pTagData );
  8898.                 m_originalTagType = P_UNKNOWN;
  8899.                 free( pTagData );
  8900.             }
  8901.             else {
  8902.                 SetTagData( locked_buff );
  8903.             }
  8904.         }
  8905.         PA_UNLOCK(pTag->data);
  8906.         SetSpoofData( pTag );
  8907.     }
  8908. }
  8909.  
  8910. CEditIconElement::~CEditIconElement(){
  8911.     DisconnectLayoutElements((LO_Element*) m_pLoIcon);
  8912.     if( m_pSpoofData ){
  8913.         free( m_pSpoofData );
  8914.     }
  8915.     XP_FREEIF(m_piSaveIndices);
  8916. }
  8917.  
  8918. CEditIconElement::CEditIconElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer):
  8919.             CEditLeafElement(pStreamIn, pBuffer), 
  8920.             m_pLoIcon(0),
  8921.             m_piSaveIndices(NULL)
  8922.             {
  8923.     m_originalTagType = (TagType) pStreamIn->ReadInt();
  8924.     m_iconTag = pStreamIn->ReadInt();
  8925.     m_bEndTag = (XP_Bool) pStreamIn->ReadInt();
  8926.     m_pSpoofData = pStreamIn->ReadZString();
  8927. }
  8928.  
  8929. void CEditIconElement::SetLayoutElement( intn iEditOffset, intn lo_type, 
  8930.                         LO_Element* pLoElement ){
  8931.     SetLayoutElementHelper(LO_IMAGE, (LO_Element**) &m_pLoIcon,
  8932.         iEditOffset, lo_type, pLoElement);
  8933. }
  8934.  
  8935. void CEditIconElement::ResetLayoutElement( intn iEditOffset, 
  8936.             LO_Element* pLoElement ){
  8937.     ResetLayoutElementHelper((LO_Element**) &m_pLoIcon,
  8938.         iEditOffset, pLoElement);
  8939. }
  8940.  
  8941. void CEditIconElement::StreamOut( IStreamOut *pOut ){
  8942.     CEditLeafElement::StreamOut( pOut );
  8943.     pOut->WriteInt( m_originalTagType );
  8944.     pOut->WriteInt( m_iconTag );
  8945.     pOut->WriteInt( m_bEndTag );
  8946.     pOut->WriteZString( m_pSpoofData );
  8947. }
  8948.  
  8949. XP_Bool CEditIconElement::GetLOElementAndOffset( ElementOffset iEditOffset, XP_Bool /* bEditStickyAfter */,
  8950.             LO_Element*& pRetElement,
  8951.             int& pLayoutOffset ){
  8952.     pLayoutOffset = 0;
  8953.     pRetElement = (LO_Element*)m_pLoIcon;
  8954.     pLayoutOffset = iEditOffset;
  8955.     return TRUE;
  8956. }
  8957.  
  8958. PA_Tag* CEditIconElement::TagOpen( int /* iEditOffset */ ){
  8959.     PA_Tag *pTag = XP_NEW( PA_Tag );
  8960.     XP_BZERO( pTag, sizeof( PA_Tag ) );
  8961.     SetTagData( pTag, m_pSpoofData );
  8962.     return pTag;
  8963. }
  8964.  
  8965. #define IS_WHITE(p) ((p) == ' ' || (p) == '\r' || (p) == '\n')
  8966.  
  8967. PRIVATE XP_Bool IsScriptText2(char* pData, char* pName){
  8968.     char *p = pData;
  8969.  
  8970.     while( *p && IS_WHITE(*p) ){
  8971.         p++;
  8972.     }
  8973.     if ( !*p || *p != '<' ) {
  8974.         return FALSE;
  8975.     }
  8976.  
  8977.     p++;
  8978.     char *n = pName;
  8979.  
  8980.     while ( *p && * n && ! IS_WHITE(*p) && XP_TO_LOWER(*p) == XP_TO_LOWER(*n) ) {
  8981.         p++;
  8982.         n++;
  8983.     }
  8984.     if ( *n != 0 || (*p != '>' && ! IS_WHITE(*p)) ) {
  8985.         // We didn't match the tag!
  8986.         return FALSE;
  8987.     }
  8988.     // Go to the end of the string.
  8989.     while ( *p ) {
  8990.         p++;
  8991.     }
  8992.     p--;
  8993.     // Back up to the last '>'. Nothing but whitespace to last '>'
  8994.     while ( p > pData && (*p != '>' && IS_WHITE(*p) ) ) {
  8995.         --p;
  8996.     }
  8997.     if ( p <= pData || *p != '>' ) {
  8998.         return FALSE;
  8999.     }
  9000.     // Back-match to last '<'
  9001.     while ( *p && *p != '<' && p > pData ) {
  9002.         p--;
  9003.     }
  9004.     if ( p <= pData || *p != '<' ) {
  9005.         return FALSE;
  9006.     }
  9007.     if ( *++p != '/' ) {
  9008.         return FALSE;
  9009.     }
  9010.     if ( strcasestr(++p, pName) == NULL ) {
  9011.         return FALSE;
  9012.     }
  9013.     p += XP_STRLEN(pName);
  9014.     if ( *p != '>' && ! IS_WHITE(*p) ) {
  9015.         return FALSE;
  9016.     }
  9017.     return TRUE;
  9018. }
  9019.  
  9020. PRIVATE XP_Bool IsScriptText(char* pData) {
  9021.     // Return TRUE if pData is <SCRIPT>...</SCRIPT>, or <STYLE>...</STYLE>, or <SERVER>...</SERVER>
  9022.     return IsScriptText2(pData, "SCRIPT") || IsScriptText2(pData, "STYLE") || IsScriptText2(pData, "SERVER");
  9023. }
  9024. //
  9025. // Do some heuristic validation to make sure 
  9026. //
  9027. ED_TagValidateResult CEditIconElement::ValidateTag( char *pData, XP_Bool bNoBrackets ){
  9028.     if ( IsScriptText(pData) ) {
  9029.         return ED_TAG_OK;
  9030.     }
  9031.     char *p = pData;
  9032.  
  9033.     while( *p && IS_WHITE(*p) ){
  9034.         p++;
  9035.     }
  9036.     if( !bNoBrackets ){
  9037.         if( *p != '<' ){
  9038.             return ED_TAG_UNOPENED;
  9039.         }
  9040.         else {
  9041.             p++;
  9042.         }
  9043.         if( *p == '/' ){
  9044.             p++;
  9045.         }
  9046.  
  9047.         if( IS_WHITE(*p) ){
  9048.             return ED_TAG_TAGNAME_EXPECTED;
  9049.         }
  9050.     }
  9051.  
  9052.     // Is this a comment?
  9053.     XP_Bool isComment = p[0] == '!' && p[1] == '-' && p[2] == '-';
  9054.  
  9055.     if ( isComment ) {
  9056.         char* start = p;
  9057.         while( *p ){
  9058.             p++;
  9059.         }
  9060.         if ( ! bNoBrackets ) {
  9061.             --p;
  9062.         }
  9063.  
  9064.         while ( IS_WHITE(*p) && p > start ) {
  9065.             p--;
  9066.         }
  9067.  
  9068.         XP_Bool endComment = (p >= start + 6) && p[-2] == '-' && p[-1] == '-';
  9069.         if ( ! endComment ) {
  9070.             return ED_TAG_PREMATURE_CLOSE; // ToDo: Use a specific error message
  9071.         }
  9072.     }
  9073.     else {
  9074.         // look for unterminated strings.
  9075.         while( *p && *p != '>' ){
  9076.             if( *p == '"' || *p == '\'' ){
  9077.                 char quote = *p++;
  9078.                 while( *p && *p != quote ){
  9079.                     p++;
  9080.                 }
  9081.                 if( *p == 0 ){
  9082.                     return ED_TAG_UNTERMINATED_STRING;
  9083.                 }
  9084.             }
  9085.             p++;
  9086.         }
  9087.     }
  9088.     if( bNoBrackets ){
  9089.         if( *p == 0 ){
  9090.             return ED_TAG_OK;
  9091.         }
  9092.         else {
  9093.             XP_ASSERT( *p == '>' );
  9094.             return ED_TAG_PREMATURE_CLOSE;
  9095.         }
  9096.     }   
  9097.     else if( *p == '>' ){
  9098.         p++;
  9099.         while( IS_WHITE( *p ) ){
  9100.             p++;
  9101.         }
  9102.         if( *p != 0 ){
  9103.             return ED_TAG_PREMATURE_CLOSE;
  9104.         }
  9105.         else {
  9106.             return ED_TAG_OK;
  9107.         }
  9108.     }
  9109.     else {
  9110.         XP_ASSERT( *p == 0 );
  9111.         return ED_TAG_UNCLOSED;
  9112.     }
  9113. }
  9114.  
  9115. //
  9116. // Restore the original tag type so we save the right tag type when we output
  9117. //  we print.
  9118. //
  9119. void CEditIconElement::PrintOpen( CPrintState *pPrintState ){
  9120.     if( m_originalTagType != P_UNKNOWN ){
  9121.         TagType tSave = GetType();
  9122.         SetType( m_originalTagType );
  9123.         CEditLeafElement::PrintOpen( pPrintState );
  9124.         SetType( tSave );
  9125.     }
  9126.     else {
  9127.         if( m_bEndTag ){
  9128.             pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "</%s", 
  9129.                 GetTagData());
  9130.         }
  9131.         else {
  9132.             pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "<%s", 
  9133.                 GetTagData());
  9134.         }
  9135.     }
  9136. }
  9137.  
  9138. void CEditIconElement::PrintEnd( CPrintState *pPrintState ){
  9139.     if( m_originalTagType != P_UNKNOWN ){
  9140.         TagType tSave = GetType();
  9141.         SetType( m_originalTagType );
  9142.         CEditLeafElement::PrintEnd( pPrintState );
  9143.         SetType( tSave );
  9144.     }
  9145. }
  9146.  
  9147. char* CEditIconElement::GetData(){
  9148.     int iLen = XP_STRLEN( GetTagData() );
  9149.     char *p = (char*)XP_ALLOC( iLen+3 );
  9150.     char *pRet = p;
  9151.     *p++ = '<';
  9152.     if( m_bEndTag ){
  9153.         *p++ = '/';
  9154.     }
  9155.     XP_STRCPY( p, GetTagData() );
  9156.     return pRet;
  9157. }
  9158.  
  9159.  
  9160. PRIVATE
  9161. void edt_addToStringLists(char **list1,char *start1,char *end1,
  9162.                           char **list2,char *start2,char *end2,
  9163.                           int index) {
  9164.   int len1 = end1 - start1; 
  9165.   int len2 = end2 - start2;
  9166.  
  9167.   // Don't allow empty strings.
  9168.   if ((len1 > 0) && (len2 > 0)) {
  9169.     // space for trailing NULL.
  9170.     list1[index] = (char *)XP_ALLOC(len1 + 1);
  9171.     list2[index] = (char *)XP_ALLOC(len2 + 1);
  9172.     if (!list1[index] || !list2[index]) {
  9173.       XP_ASSERT(0);
  9174.       return;
  9175.     }
  9176.     // copy strings.
  9177.     XP_MEMCPY(list1[index],start1,len1);
  9178.     list1[index][len1] = '\0';
  9179.     XP_MEMCPY(list2[index],start2,len2);
  9180.     list2[index][len2] = '\0';
  9181.   }
  9182. }
  9183.  
  9184. int CEditIconElement::ParseLocalData(char ***mimeTypes,char ***URLs) {
  9185.   int retVal = 0;      
  9186.   *mimeTypes = NULL;
  9187.   *URLs = NULL;
  9188.  
  9189.   // Don't want the version with the spoofData, so call ancestor.
  9190.   PA_Tag *pTag = CEditElement::TagOpen(0);
  9191.  
  9192.   if (pTag) {
  9193.     // Don't use edt_FetchParamString because it strips out interior 
  9194.     // white space.
  9195.     PA_Block buff = PA_FetchParamValue(pTag,PARAM_LOCALDATA,GetWinCSID());
  9196.     if (buff) {
  9197.       char *pLocalData;
  9198.       PA_LOCK(pLocalData,char *,buff);
  9199.  
  9200.       // maxFiles = 1+number of '+' characters in pLocalData,
  9201.       // gives upper bound on size of returned lists.
  9202.       int maxFiles = 1;
  9203.       char *p = pLocalData;
  9204.       while (*p && (p = XP_STRCHR(p,'+')) != NULL) {
  9205.         maxFiles++;
  9206.         p++; // move past found '+'
  9207.       }
  9208.  
  9209.       // Create lists.
  9210.       *mimeTypes = (char **)XP_ALLOC(maxFiles * sizeof(char *));
  9211.       if (!*mimeTypes) {
  9212.         return 0;
  9213.       }
  9214.       *URLs = (char **)XP_ALLOC(maxFiles * sizeof(char *));
  9215.       if (!*URLs) {
  9216.         XP_FREEIF(*mimeTypes);  // Will zero *mimeTypes.
  9217.         return 0;
  9218.       }
  9219.  
  9220.  
  9221.       // reset p.
  9222.       p = pLocalData;
  9223.  
  9224.       char *mimeStart,*mimeEnd,*URLStart,*URLEnd;
  9225.  
  9226.       // State machine to read in string
  9227.       int state = BeforeMIME;
  9228.       while(*p) {
  9229.         switch (state) {
  9230.           case BeforeMIME:
  9231.             if (!XP_IS_SPACE(*p)) {
  9232.               state = InMIME;
  9233.               mimeStart = p;
  9234.             }
  9235.           break;
  9236.           case InMIME:
  9237.             if (XP_IS_SPACE(*p)) {
  9238.               mimeEnd = p;
  9239.               state = BeforeURL;
  9240.             }
  9241.           break;
  9242.           case BeforeURL:
  9243.             if (!XP_IS_SPACE(*p)) {
  9244.               URLStart = p;
  9245.               state = InURL;
  9246.             }
  9247.           break;
  9248.           case InURL:
  9249.             if (XP_IS_SPACE(*p)) {
  9250.               URLEnd = p;
  9251.               XP_ASSERT(retVal < maxFiles);
  9252.               edt_addToStringLists(*mimeTypes,mimeStart,mimeEnd,
  9253.                                    *URLs,URLStart,URLEnd,
  9254.                                    retVal);
  9255.               retVal++;
  9256.               state = AfterURL;
  9257.             }
  9258.             if (*p == '+') {
  9259.               URLEnd = p;
  9260.               XP_ASSERT(retVal < maxFiles);
  9261.               edt_addToStringLists(*mimeTypes,mimeStart,mimeEnd,
  9262.                                    *URLs,URLStart,URLEnd,
  9263.                                    retVal);
  9264.               retVal++;
  9265.               state = BeforeMIME;
  9266.             }
  9267.           break;
  9268.           case AfterURL:
  9269.             if (*p == '+') {
  9270.               state = BeforeMIME;
  9271.             }
  9272.           break;
  9273.           default:
  9274.             XP_ASSERT(0);
  9275.           break;
  9276.         }
  9277.  
  9278.         // Move forward.
  9279.         p++;
  9280.       }
  9281.  
  9282.       // Close out final mime/URL pair.
  9283.       if (state == InURL) {
  9284.         URLEnd = p;
  9285.         XP_ASSERT(retVal < maxFiles);
  9286.         edt_addToStringLists(*mimeTypes,mimeStart,mimeEnd,
  9287.                              *URLs,URLStart,URLEnd,
  9288.                              retVal);
  9289.         retVal++;
  9290.       }
  9291.  
  9292.  
  9293.       PA_UNLOCK(buff);
  9294.       PA_FREE(buff);
  9295.     }
  9296.     PA_FreeTag(pTag);
  9297.   }
  9298.  
  9299.   return retVal;
  9300. }
  9301.  
  9302. void CEditIconElement::FreeLocalDataLists(char **mimeTypes,char **URLs,int count) {
  9303.   for (int n = 0; n < count; n++) {
  9304.     XP_FREEIF(mimeTypes[n]);
  9305.     XP_FREEIF(URLs[n]);
  9306.   }
  9307.   XP_FREEIF(mimeTypes);
  9308.   XP_FREEIF(URLs);
  9309. }
  9310.  
  9311. void CEditIconElement::ReplaceParamValues(char *pOld,char *pNew) {
  9312.   // Not changing anything.
  9313.   if (!XP_STRCMP(pOld,pNew)) return;
  9314.   
  9315.   // Illegal.
  9316.   if (!pOld || !pNew || pOld[0] == '=' || pOld[0] == '\"') return;
  9317.  
  9318.   char *pTagData = GetTagData();
  9319.   if (!pTagData) return;
  9320.  
  9321.   int state = OutsideValue;
  9322.   char *last = pTagData; // last part copied to pNewTagData.
  9323.   char *scan = pTagData; // Scans ahead of last.
  9324.   char *pNewTagData = NULL;
  9325.   int pOldLen = XP_STRLEN(pOld);
  9326.  
  9327.   StrAllocCat(pNewTagData,"<");
  9328.  
  9329.   while (*scan) {
  9330.     XP_ASSERT(scan);
  9331.  
  9332.     switch (state) {
  9333.       case OutsideValue: 
  9334.         if (*scan == '=') {
  9335.           state = BeforeValue;
  9336.         }
  9337.         break;
  9338.       case InsideValue:
  9339.         if (XP_IS_SPACE(*scan)) {
  9340.           // end of non-quoted value.
  9341.           state = OutsideValue;
  9342.         }
  9343.         break;
  9344.       case InsideValueQuote:
  9345.         if (*scan == '\"') {
  9346.           // closing quote.
  9347.           state = OutsideValue;
  9348.         }
  9349.         break;
  9350.       case BeforeValue:
  9351.         if (*scan == '\"') {
  9352.           state = InsideValueQuote;
  9353.         }
  9354.         else if (!XP_IS_SPACE(*scan)) {
  9355.           state = InsideValue;
  9356.         }
  9357.         // else still BeforeValue
  9358.         break;
  9359.       default:
  9360.         XP_ASSERT(0);
  9361.     }
  9362.  
  9363.     if (state == InsideValue || state == InsideValueQuote) {
  9364.       // Found an instance.
  9365.       if (!XP_STRNCMP(scan,pOld,pOldLen)) {
  9366.         // flush last part.
  9367.         if (last != scan) {
  9368.           char store = *scan;
  9369.           *scan = '\0';
  9370.           StrAllocCat(pNewTagData,last);
  9371.           *scan = store;
  9372.         }
  9373.         StrAllocCat(pNewTagData,pNew);
  9374.         scan += pOldLen; // skip past prev string.
  9375.         last = scan;
  9376.  
  9377.         scan--; // To counteract following scan++.
  9378.       }
  9379.     }
  9380.  
  9381.     scan++;
  9382.   }
  9383.   
  9384.   // flush last part.
  9385.   if (last != scan) {
  9386.     StrAllocCat(pNewTagData,last);
  9387.     last = scan;
  9388.   }
  9389.  
  9390.   SetData(pNewTagData);  
  9391.   XP_FREEIF(pNewTagData);
  9392. }
  9393.  
  9394.  
  9395. PRIVATE
  9396. XP_Bool IsHTMLCommentTag(char* string) {
  9397.     return string && string[0] == '!'
  9398.         && string[1] == '-'
  9399.         && string[2] == '-';
  9400. }
  9401.  
  9402. void CEditIconElement::SetSpoofData( PA_Tag* pTag ){
  9403.     
  9404.     if( m_iconTag == EDT_ICON_UNSUPPORTED_TAG 
  9405.             || m_iconTag == EDT_ICON_UNSUPPORTED_END_TAG ){
  9406.  
  9407.         char *pTagString;
  9408.         PA_LOCK(pTagString, char *, pTag->data );
  9409.     
  9410.         char *pData = PR_smprintf( ppIconTags[m_iconTag], 
  9411.                     edt_QuoteString( pTagString ));
  9412.         
  9413.         XP_Bool bDimensionFound = FALSE;
  9414.         XP_Bool bComment = m_originalTagType == P_UNKNOWN &&
  9415.             IsHTMLCommentTag(pTagString);
  9416.  
  9417.         if ( ! bComment ) {
  9418.             int32 height;
  9419.             int32 width;
  9420.             XP_Bool bPercent;
  9421.         
  9422.             edt_FetchDimension( pTag, PARAM_HEIGHT, &height, &bPercent, -1, TRUE, GetWinCSID() );
  9423.             if( height != -1 ){
  9424.                 pData = PR_sprintf_append( pData, " HEIGHT=%d%s", height, 
  9425.                             (bPercent ? "%" : "" ) );
  9426.                 bDimensionFound = TRUE;
  9427.             }
  9428.  
  9429.             edt_FetchDimension( pTag, PARAM_WIDTH, &width, &bPercent, -1, TRUE, GetWinCSID() );
  9430.             if( width != -1 ){
  9431.                 pData = PR_sprintf_append( pData, " WIDTH=%d%s", width, 
  9432.                         (bPercent ? "%" : "" ) );
  9433.                 bDimensionFound = TRUE;
  9434.             }
  9435.         }
  9436.  
  9437.         if( bDimensionFound ){
  9438.             pData = PR_sprintf_append(pData, " BORDER=2>");
  9439.         }
  9440.         else {
  9441.             pData = PR_sprintf_append(pData, " BORDER=0 ALIGN=ABSBOTTOM>");
  9442.         }
  9443.  
  9444.         if( m_pSpoofData ){
  9445.             free( m_pSpoofData );
  9446.         }
  9447.         m_pSpoofData = pData;
  9448.         PA_UNLOCK(pTag->data);
  9449.     }
  9450.     else {
  9451.         m_pSpoofData = XP_STRDUP( ppIconTags[m_iconTag] );
  9452.     }
  9453.  
  9454. }
  9455.  
  9456. void CEditIconElement::SetSpoofData( char* pData ){
  9457.     char *pNewData = PR_smprintf( ppIconTags[m_iconTag], 
  9458.                 edt_QuoteString( pData ));
  9459.     if( m_pSpoofData ){
  9460.         XP_FREE( m_pSpoofData );
  9461.     }
  9462.     m_pSpoofData = pNewData;
  9463. }
  9464.  
  9465. void CEditIconElement::SetData( char *pData ){
  9466.     // Since validater skips white space, so should SetData
  9467.     while( *pData && IS_WHITE(*pData) ){
  9468.         pData++;
  9469.     }
  9470.  
  9471.     // If you assert here, you aren't validating your tag.
  9472.     if( pData[0] != '<' ) {
  9473.         XP_ASSERT(FALSE);
  9474.         return;
  9475.     }
  9476.  
  9477.     pData++;
  9478.  
  9479.     if( *pData == '/' ){
  9480.         pData++;
  9481.         m_bEndTag = TRUE;
  9482.     }
  9483.     SetTagData( pData );
  9484.  
  9485.  
  9486.     // Build up a tag that we can fetch parameter strings from.
  9487.     PA_Tag *pTag = XP_NEW( PA_Tag );
  9488.     XP_BZERO( pTag, sizeof( PA_Tag ) );
  9489.     SetTagData( pTag, pData );
  9490.     pTag->is_end = m_bEndTag;
  9491.  
  9492.     SetSpoofData( pTag );
  9493.     PA_FreeTag( pTag );
  9494. }
  9495.  
  9496. //
  9497. // We need to convert a tag handed to us by the parser into a tag we can
  9498. //  display in the editor.  In order to do this, we 'morph' the tag by swaping
  9499. //  the contents with desired tag.  We need an additional tag for swap space.
  9500. //  All this is done to insure that the memory location of 'pTag' doesn't 
  9501. //  change.  Perhaps this routine should be implemented at the CEditElement
  9502. //  layer.
  9503. //             
  9504. void CEditIconElement::MorphTag( PA_Tag *pTag ){
  9505.     PA_Tag *pNewTag = TagOpen(0);
  9506.     PA_Tag tempTag;
  9507.  
  9508.     tempTag = *pTag;
  9509.     *pTag = *pNewTag;
  9510.     *pNewTag = tempTag;
  9511.  
  9512.     PA_FreeTag( pNewTag );
  9513. }             
  9514.    
  9515. XP_Bool CEditIconElement::IsUnknownHTML(){
  9516.     return m_iconTag == EDT_ICON_UNSUPPORTED_TAG
  9517.         || m_iconTag == EDT_ICON_UNSUPPORTED_END_TAG;
  9518. }
  9519.  
  9520. XP_Bool CEditIconElement::IsComment(){
  9521.     return IsUnknownHTML() && IsHTMLCommentTag(GetTagData());
  9522. }
  9523.  
  9524. XP_Bool CEditIconElement::IsComment(char* prefix){
  9525.     if ( ! IsComment() ) return FALSE;
  9526.     char* pData = GetTagData();
  9527.     if ( ! prefix || ! pData ) return FALSE;
  9528.     int32 prefixLen = XP_STRLEN(prefix);
  9529.     int32 dataLen = XP_STRLEN(pData);
  9530.     // Skip past "!--"
  9531.     if ( dataLen <= 3 ) return FALSE;
  9532.     dataLen -= 3;
  9533.     pData += 3;
  9534.     // Skip whitespace
  9535.     while ( dataLen > 0 && *pData == ' ' ){
  9536.         pData++;
  9537.         dataLen--;
  9538.     }
  9539.     if ( prefixLen > dataLen ) return FALSE;
  9540.     if ( XP_STRNCMP(prefix, pData, prefixLen) == 0 ){
  9541.         return TRUE;
  9542.     }
  9543.     return FALSE;
  9544. }
  9545.  
  9546. void CEditIconElement::SetSize(XP_Bool bWidthPercent, int32 iWidth,
  9547.                                XP_Bool bHeightPercent, int32 iHeight){
  9548.     
  9549.     // Do NOT use TagOpen -- this  gets the "m_pSpoofData" junk
  9550.     PA_Tag *pTag = XP_NEW( PA_Tag );
  9551.     XP_BZERO( pTag, sizeof( PA_Tag ) );
  9552.     // This string has initial "<"
  9553.     SetTagData( pTag, GetData() );
  9554.  
  9555.     if( pTag ){    
  9556.         char *pWidth = 0;
  9557.         char *pHeight = 0;
  9558.         if( iWidth > 0 ){
  9559.             pWidth = PR_sprintf_append( pWidth, "%ld%s", (long)iWidth, bWidthPercent ? "%%" : "" );
  9560.             if( pWidth ){
  9561.                 edt_ReplaceParamValue( pTag, "WIDTH", pWidth, GetWinCSID());
  9562.             }
  9563.         }
  9564.         if( iHeight > 0){
  9565.             pHeight = PR_sprintf_append( pHeight, "%ld%s", (long)iHeight, bHeightPercent ? "%%" : "" );
  9566.             if( pHeight ){
  9567.                 edt_ReplaceParamValue( pTag, "HEIGHT", pHeight, GetWinCSID());
  9568.             }
  9569.         }
  9570.         if( pWidth || pHeight ){
  9571.             SetData((char*)pTag->data);
  9572.             if( pWidth ) XP_FREE(pWidth);
  9573.             if( pHeight ) XP_FREE(pHeight);
  9574.         }
  9575.         PA_FREE(pTag);
  9576.     }
  9577. }
  9578.  
  9579.  
  9580. //-----------------------------------------------------------------------------
  9581. // CEditTargetElement
  9582. //-----------------------------------------------------------------------------
  9583. CEditTargetElement::CEditTargetElement(CEditElement *pParent, PA_Tag* pTag, int16 csid)
  9584.         :
  9585.             CEditIconElement( pParent, EDT_ICON_NAMED_ANCHOR,  pTag )
  9586.         {
  9587.         m_originalTagType = P_ANCHOR;
  9588.         // To get the spoof tag set up correctly
  9589.         EDT_TargetData* pData = GetData(csid);
  9590.         SetData( pData );
  9591.         FreeTargetData(pData);
  9592. }
  9593.  
  9594. CEditTargetElement::CEditTargetElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer)
  9595.         :
  9596.             CEditIconElement( pStreamIn, pBuffer )
  9597.         {
  9598. }
  9599.  
  9600. void CEditTargetElement::StreamOut( IStreamOut *pOut){
  9601.     CEditIconElement::StreamOut( pOut );
  9602. }
  9603.  
  9604. PA_Tag* CEditTargetElement::TagOpen( int iEditOffset ){
  9605.     // We need to create the actual anchor tag set.
  9606.     return CEditIconElement::TagOpen( iEditOffset );
  9607. }
  9608.  
  9609. void CEditTargetElement::SetName( char* pName, int16 csid ){
  9610.     if ( pName ) {
  9611.         EDT_TargetData* pData = GetData(csid);
  9612.         if ( pData ) {
  9613.             if ( pData->pName ) {
  9614.                 XP_FREE(pData->pName);
  9615.             }
  9616.             pData->pName = XP_STRDUP(pName);
  9617.         }
  9618.         SetData(pData);
  9619.         FreeTargetData(pData);
  9620.     }
  9621. }
  9622.  
  9623. void CEditTargetElement::SetData( EDT_TargetData *pData ){
  9624.     char *pNew = 0;
  9625.     if ( pData->pName) {
  9626.         pNew = PR_sprintf_append( pNew, "NAME=%s ", edt_MakeParamString(pData->pName));
  9627.     }
  9628.  
  9629.     if( pData->pExtra  ){
  9630.         pNew = PR_sprintf_append( pNew, "%s ", pData->pExtra );
  9631.     }
  9632.  
  9633.     if( pNew ){
  9634.         pNew = PR_sprintf_append( pNew, ">" );
  9635.     }
  9636.     SetTagData( pNew );
  9637.     if ( pNew ) {
  9638.         free(pNew);
  9639.     }
  9640.     // Set the spoof data for the icon.
  9641.     CEditIconElement::SetSpoofData( pData->pName ? pData->pName : "" );
  9642. }
  9643.  
  9644. char* CEditTargetElement::GetName(){
  9645.     char* pName = 0;
  9646.     EDT_TargetData* pData = GetData();
  9647.     if (pData && pData->pName) {
  9648.         pName = XP_STRDUP(pData->pName);
  9649.     }
  9650.     FreeTargetData(pData);
  9651.     return pName;
  9652. }
  9653.  
  9654. EDT_TargetData* CEditTargetElement::GetData(){
  9655.     return GetData(GetWinCSID());
  9656. }
  9657.  
  9658. EDT_TargetData* CEditTargetElement::GetData(int16 csid){
  9659.     EDT_TargetData *pRet;
  9660.     // Get the actual tag data, not the faked up stuff at the Icon layer.
  9661.     PA_Tag* pTag = CEditLeafElement::TagOpen(0);
  9662.     pRet = ParseParams( pTag, csid );
  9663.     PA_FreeTag( pTag );
  9664.     return pRet;
  9665. }
  9666.  
  9667.  
  9668. static char *targetParams[] = {
  9669.     PARAM_NAME,
  9670.     0
  9671. };
  9672.  
  9673. EDT_TargetData* CEditTargetElement::ParseParams( PA_Tag *pTag, int16 csid ){
  9674.     EDT_TargetData* pData = NewTargetData();
  9675.     pData->pName = edt_FetchParamString( pTag, PARAM_NAME, csid );
  9676.     pData->pExtra = edt_FetchParamExtras( pTag, targetParams, csid );
  9677.  
  9678.     return pData;
  9679. }
  9680.  
  9681. EDT_TargetData* CEditTargetElement::NewTargetData(){
  9682.     EDT_TargetData* pData = (EDT_TargetData*) XP_ALLOC(sizeof(EDT_TargetData));
  9683.     if ( pData ) {
  9684.         pData->pName = 0;
  9685.         pData->pExtra = 0;
  9686.     }
  9687.     return pData;
  9688. }
  9689.  
  9690. void CEditTargetElement::FreeTargetData(EDT_TargetData* pData){
  9691.     if ( pData ) {
  9692.         if ( pData->pName ) {
  9693.             XP_FREE(pData->pName);
  9694.         }
  9695.         if ( pData->pExtra ) {
  9696.             XP_FREE(pData->pExtra);
  9697.         }
  9698.         XP_FREE(pData);
  9699.     }
  9700. }
  9701.  
  9702. //-----------------------------------------------------------------------------
  9703. // CEditBreakElement
  9704. //-----------------------------------------------------------------------------
  9705.  
  9706. CEditBreakElement::CEditBreakElement( CEditElement *pParent, PA_Tag* pTag, int16 /*csid*/ ):
  9707.     CEditLeafElement( pParent, P_LINEBREAK ),
  9708.     m_pLoLinefeed(0)
  9709. {
  9710.     if( pTag ){
  9711.         char *locked_buff;
  9712.                 
  9713.         PA_LOCK(locked_buff, char *, pTag->data );
  9714.         if( locked_buff ){
  9715.             SetTagData( locked_buff );
  9716.         }
  9717.         PA_UNLOCK(pTag->data);
  9718.     }
  9719. }
  9720.  
  9721. CEditBreakElement::CEditBreakElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer):
  9722.     CEditLeafElement(pStreamIn, pBuffer),
  9723.     m_pLoLinefeed(0)
  9724. {
  9725. }
  9726.  
  9727. CEditBreakElement::~CEditBreakElement(){
  9728.     DisconnectLayoutElements((LO_Element*) m_pLoLinefeed);
  9729. }
  9730.  
  9731.  
  9732. void CEditBreakElement::StreamOut( IStreamOut *pOut){
  9733.     CEditLeafElement::StreamOut( pOut );
  9734. }
  9735.  
  9736. EEditElementType CEditBreakElement::GetElementType(){
  9737.     return eBreakElement;
  9738. }
  9739.  
  9740. void CEditBreakElement::SetLayoutElement( intn iEditOffset, intn lo_type, 
  9741.                         LO_Element* pLoElement ){
  9742.     SetLayoutElementHelper(LO_LINEFEED, (LO_Element**) &m_pLoLinefeed,
  9743.         iEditOffset, lo_type, pLoElement);
  9744. }
  9745.  
  9746. void CEditBreakElement::ResetLayoutElement( intn iEditOffset, 
  9747.             LO_Element* pLoElement ){
  9748.     ResetLayoutElementHelper((LO_Element**) &m_pLoLinefeed,
  9749.         iEditOffset, pLoElement);
  9750. }
  9751.  
  9752. LO_Element* CEditBreakElement::GetLayoutElement(){
  9753.     return (LO_Element*)m_pLoLinefeed;
  9754. }
  9755.  
  9756. XP_Bool CEditBreakElement::GetLOElementAndOffset( ElementOffset iEditOffset, XP_Bool /* bStickyAfter */,
  9757.             LO_Element*& pRetElement, 
  9758.             int& pLayoutOffset ){ 
  9759.     pLayoutOffset = 0; 
  9760.     pRetElement = (LO_Element*)m_pLoLinefeed;
  9761.     pLayoutOffset = iEditOffset;
  9762.     return TRUE;
  9763. }
  9764.  
  9765. //
  9766. // if we are within PREFORMAT (or the like) breaks are returns.
  9767. //
  9768. void CEditBreakElement::PrintOpen( CPrintState *ps ){
  9769.     CEditElement *pPrev = PreviousLeafInContainer();
  9770.     while( pPrev && pPrev->IsA(P_LINEBREAK) ){
  9771.         pPrev = pPrev->PreviousLeafInContainer();
  9772.     }
  9773.     CEditElement *pNext = LeafInContainerAfter();
  9774.     while( pNext && pNext->IsA(P_LINEBREAK) ){
  9775.         pNext = pNext->LeafInContainerAfter();
  9776.     }
  9777. #ifdef USE_SCRIPT
  9778.     XP_Bool bScriptBefore = pPrev 
  9779.                 && pPrev->IsA(P_TEXT) 
  9780.                 && (pPrev->Text()->m_tf & (TF_SERVER|TF_SCRIPT|TF_STYLE));
  9781.     XP_Bool bScriptAfter = pNext && pNext->IsA(P_TEXT) && (pNext->Text()->m_tf & (TF_SERVER|TF_SCRIPT|TF_STYLE));
  9782.     
  9783.     if ( !bScriptBefore && bScriptAfter ){
  9784.         // Don't print <BR>s before <SCRIPT> tags. This swallows both legal <BR> tags,
  9785.         // and also swallows '\n' at the start of scripts. If we didn't do this,
  9786.         // then we would turn <SCRIPT>\nFoo into <BR><SCRIPT>Foo.
  9787.         // The case of a legitimate <BR> before a <SCRIPT> tag is much rarer than the
  9788.         // case of a \n just after the <SCRIPT> tag. So we err on the side that lets
  9789.         // more pages work.
  9790.         // Take this code out when text styles are extended to all leaf elements.
  9791.         return;
  9792.     }
  9793. #endif
  9794. #ifdef USE_SCRIPT
  9795.     if( InFormattedText() 
  9796.             || bScriptBefore ){
  9797. #else
  9798.     if( InFormattedText() ){
  9799. #endif
  9800.         ps->m_pOut->Write( "\n", 1 );
  9801.         ps->m_iCharPos = 0;
  9802.     }
  9803.     else {
  9804.         CEditLeafElement::PrintOpen( ps );
  9805.         // if we are the last break in a container, it is ingnored by the 
  9806.         //  browser, so emit another one.
  9807.         if( GetNextSibling() == 0 ){
  9808.             ps->m_pOut->Write( "<BR>", 4 );
  9809.         }
  9810.     }
  9811. }
  9812.  
  9813. CEditInternalAnchorElement::CEditInternalAnchorElement(CEditElement *pParent)
  9814. : CEditLeafElement(pParent, P_UNKNOWN)
  9815. {
  9816. }
  9817.  
  9818. CEditInternalAnchorElement::~CEditInternalAnchorElement()
  9819. {
  9820. }
  9821.  
  9822. XP_Bool CEditInternalAnchorElement::Reduce( CEditBuffer* /* pBuffer */ ){
  9823.     return FALSE;
  9824. }
  9825.  
  9826. XP_Bool CEditInternalAnchorElement::ShouldStreamSelf( CEditSelection& /*local*/, CEditSelection& /*selection*/){
  9827.     return FALSE;
  9828. }
  9829.  
  9830. void CEditInternalAnchorElement::StreamOut( IStreamOut * /*pOut*/){
  9831.     XP_ASSERT(FALSE);
  9832. }
  9833.  
  9834. void CEditInternalAnchorElement::SetLayoutElement( intn /*iEditOffset*/, intn /*lo_type*/, 
  9835.                 LO_Element* /*pLoElement*/ ){
  9836.         XP_ASSERT(FALSE);
  9837. }
  9838.  
  9839. void CEditInternalAnchorElement::ResetLayoutElement( intn /*iEditOffset*/, 
  9840.             LO_Element* /*pLoElement*/ ){
  9841. }
  9842.  
  9843. LO_Element* CEditInternalAnchorElement::GetLayoutElement(){
  9844.     return NULL;
  9845. }
  9846.  
  9847. XP_Bool CEditInternalAnchorElement::GetLOElementAndOffset( ElementOffset /*iEditOffset*/, XP_Bool /*bEditStickyAfter*/,
  9848.             LO_Element*& /*pRetElement*/, 
  9849.             int& /*pLayoutOffset*/ ){
  9850.     return FALSE;
  9851. }
  9852.  
  9853. EEditElementType CEditInternalAnchorElement::GetElementType() {
  9854.     return eInternalAnchorElement;
  9855. }
  9856.  
  9857. void CEditInternalAnchorElement::PrintOpen( CPrintState * /*pPrintState*/ ){
  9858. }
  9859.  
  9860. void CEditInternalAnchorElement::PrintEnd( CPrintState * /*pPrintState*/ ){
  9861. }
  9862.  
  9863. //---------------------------------------------------------------------------
  9864. // CEditEndContainerElement
  9865. //---------------------------------------------------------------------------
  9866.  
  9867. CEditEndContainerElement::CEditEndContainerElement(CEditElement *pParent) :
  9868.         CEditContainerElement(pParent, NULL, 0 /* Never used for an end container */, ED_ALIGN_LEFT)
  9869.         {
  9870. }
  9871.  
  9872. void CEditEndContainerElement::StreamOut( IStreamOut * /*pOut*/ ) {
  9873.     XP_ASSERT(FALSE);
  9874. }
  9875.  
  9876. XP_Bool CEditEndContainerElement::ShouldStreamSelf( CEditSelection& /* local */, CEditSelection& /* selection */ ) {
  9877.     return FALSE;
  9878. }
  9879. XP_Bool CEditEndContainerElement::IsAcceptableChild(CEditElement& pChild){
  9880.     return pChild.GetElementType() == eEndElement;
  9881. }
  9882. void CEditEndContainerElement::PrintOpen( CPrintState * /* pPrintState */ ){
  9883. }
  9884. void CEditEndContainerElement::PrintEnd( CPrintState * /* pPrintState */ ){
  9885. }
  9886. XP_Bool CEditEndContainerElement::IsEndContainer() {
  9887.     return TRUE;
  9888. }
  9889. void CEditEndContainerElement::AdjustContainers( CEditBuffer* /* pBuffer */ ){
  9890. }
  9891.  
  9892. #endif //EDITOR
  9893.