mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 06:35:42 +00:00
12042 lines
363 KiB
C++
12042 lines
363 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
|
|
|
|
//
|
|
// Public interface and shared subsystem data.
|
|
//
|
|
|
|
#ifdef EDITOR
|
|
|
|
#include "editor.h"
|
|
#include "libimg.h"
|
|
#include "intl_csi.h"
|
|
#include "xp_str.h"
|
|
|
|
#ifdef USE_SCRIPT
|
|
#define EDT_IS_SCRIPT(tf) (0 != (tf & (TF_SERVER | TF_SCRIPT | TF_STYLE)))
|
|
#else
|
|
#define EDT_IS_SCRIPT(tf) (FALSE)
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CEditElement
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//
|
|
// This version of the constructor is used to create a child element.
|
|
//
|
|
CEditElement::CEditElement(CEditElement *pParent, TagType tagType, char* pData):
|
|
m_tagType(tagType),
|
|
m_pParent(pParent),
|
|
m_pNext(0),
|
|
m_pChild(0),
|
|
m_pTagData(0)
|
|
{
|
|
if ( pData ) {
|
|
SetTagData(pData);
|
|
}
|
|
CommonConstructor();
|
|
}
|
|
|
|
CEditElement::CEditElement(CEditElement *pParent, PA_Tag *pTag, int16 /*csid*/) :
|
|
m_tagType(pTag->type),
|
|
m_pParent(pParent),
|
|
m_pNext(0),
|
|
m_pChild(0),
|
|
m_pTagData(0)
|
|
{
|
|
char *locked_buff;
|
|
|
|
PA_LOCK(locked_buff, char *, pTag->data);
|
|
if( locked_buff && *locked_buff != '>'){
|
|
SetTagData( locked_buff );
|
|
}
|
|
PA_UNLOCK(pTag->data);
|
|
CommonConstructor();
|
|
}
|
|
|
|
void CEditElement::CommonConstructor(){
|
|
if( m_pParent ){
|
|
CEditElement* pE = m_pParent->GetChild();
|
|
|
|
//
|
|
// there already is a child, add this child to the end of the list.
|
|
//
|
|
if( pE ){
|
|
while( pE->m_pNext != 0 ){
|
|
pE = pE->m_pNext;
|
|
}
|
|
pE->RawSetNextSibling(this);
|
|
}
|
|
else {
|
|
// make this the first child.
|
|
m_pParent->RawSetChild(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
CEditElement::CEditElement( IStreamIn *pIn, CEditBuffer* /* pBuffer */ ):
|
|
m_pParent(0),
|
|
m_pNext(0),
|
|
m_pChild(0),
|
|
m_pTagData(0)
|
|
{
|
|
m_tagType = (TagType) pIn->ReadInt();
|
|
m_pTagData = pIn->ReadZString();
|
|
}
|
|
|
|
CEditElement::~CEditElement(){
|
|
Finalize();
|
|
}
|
|
|
|
void CEditElement::Finalize(){
|
|
Unlink();
|
|
DeleteChildren();
|
|
if( m_pTagData ) {
|
|
XP_FREE(m_pTagData);
|
|
m_pTagData = NULL;
|
|
}
|
|
}
|
|
|
|
CEditTextElement* CEditElement::Text(){
|
|
XP_ASSERT(IsText());
|
|
return (CEditTextElement*)this;
|
|
}
|
|
|
|
XP_Bool CEditElement::IsLeaf() { return FALSE; }
|
|
CEditLeafElement* CEditElement::Leaf(){ XP_ASSERT(IsLeaf());
|
|
return (CEditLeafElement*)this;
|
|
}
|
|
|
|
XP_Bool CEditElement::IsRoot() { return FALSE; }
|
|
CEditRootDocElement* CEditElement::Root(){ XP_ASSERT(IsRoot());
|
|
return (CEditRootDocElement*)this;
|
|
}
|
|
|
|
XP_Bool CEditElement::IsContainer() { return FALSE; }
|
|
CEditContainerElement* CEditElement::Container(){ XP_ASSERT(IsContainer());
|
|
return (CEditContainerElement*)this;
|
|
}
|
|
|
|
XP_Bool CEditElement::IsList() { return FALSE; }
|
|
CEditListElement* CEditElement::List(){ XP_ASSERT(IsList());
|
|
return (CEditListElement*)this;
|
|
}
|
|
|
|
XP_Bool CEditElement::IsBreak() { return FALSE; }
|
|
CEditBreakElement* CEditElement::Break(){ XP_ASSERT(IsBreak());
|
|
return (CEditBreakElement*)this;
|
|
}
|
|
|
|
XP_Bool CEditElement::CausesBreakBefore() { return FALSE;}
|
|
XP_Bool CEditElement::CausesBreakAfter() { return FALSE;}
|
|
XP_Bool CEditElement::AllowBothSidesOfGap() { return FALSE; }
|
|
|
|
XP_Bool CEditElement::IsImage() { return FALSE; }
|
|
CEditImageElement* CEditElement::Image(){
|
|
// Not all P_IMAGE elements are images. CEditIconElements have P_IMAGE
|
|
// as their tagType but are not CEditImageElements.
|
|
XP_ASSERT(m_tagType==P_IMAGE
|
|
&& GetElementType() == eImageElement
|
|
&& IsImage() ) ;
|
|
return (CEditImageElement*)this;
|
|
}
|
|
|
|
XP_Bool CEditElement::IsIcon() { return FALSE; }
|
|
CEditIconElement* CEditElement::Icon(){ XP_ASSERT(IsIcon()); return (CEditIconElement*)this;}
|
|
XP_Bool CEditElement::IsTarget() { return FALSE; }
|
|
CEditTargetElement* CEditElement::Target(){ XP_ASSERT(IsTarget()); return (CEditTargetElement*)this;}
|
|
CEditHorizRuleElement* CEditElement::HorizRule(){ XP_ASSERT(m_tagType==P_HRULE); return (CEditHorizRuleElement*)this;}
|
|
XP_Bool CEditElement::IsRootDoc() { return FALSE; }
|
|
CEditRootDocElement* CEditElement::RootDoc() { XP_ASSERT(IsRootDoc()); return (CEditRootDocElement*) this; }
|
|
XP_Bool CEditElement::IsSubDoc() { return FALSE; }
|
|
CEditSubDocElement* CEditElement::SubDoc() { XP_ASSERT(IsSubDoc()); return (CEditSubDocElement*) this; }
|
|
XP_Bool CEditElement::IsTable() { return FALSE; }
|
|
CEditTableElement* CEditElement::Table() { XP_ASSERT(IsTable()); return (CEditTableElement*) this; }
|
|
XP_Bool CEditElement::IsTableRow() { return FALSE; }
|
|
CEditTableRowElement* CEditElement::TableRow() { XP_ASSERT(IsTableRow()); return (CEditTableRowElement*) this; }
|
|
XP_Bool CEditElement::IsTableCell() { return FALSE; }
|
|
CEditTableCellElement* CEditElement::TableCell() { XP_ASSERT(IsTableCell()); return (CEditTableCellElement*) this; }
|
|
XP_Bool CEditElement::IsCaption() { return FALSE; }
|
|
CEditCaptionElement* CEditElement::Caption() { XP_ASSERT(IsCaption()); return (CEditCaptionElement*) this; }
|
|
XP_Bool CEditElement::IsText() { return FALSE; }
|
|
XP_Bool CEditElement::IsLayer() { return FALSE; }
|
|
CEditLayerElement* CEditElement::Layer() { XP_ASSERT(IsLayer()); return (CEditLayerElement*) this; }
|
|
XP_Bool CEditElement::IsDivision() { return FALSE; }
|
|
CEditDivisionElement* CEditElement::Division() { XP_ASSERT(IsDivision()); return (CEditDivisionElement*) this; }
|
|
|
|
XP_Bool CEditElement::IsEndOfDocument() { return GetElementType() == eEndElement; }
|
|
XP_Bool CEditElement::IsEndContainer() { return FALSE; }
|
|
|
|
|
|
void
|
|
CEditElement::SetTagData(char* tagData)
|
|
{
|
|
if( m_pTagData ) {
|
|
XP_FREE(m_pTagData);
|
|
}
|
|
if( tagData ){
|
|
m_pTagData = XP_STRDUP(tagData);
|
|
}
|
|
else {
|
|
m_pTagData = tagData;
|
|
}
|
|
}
|
|
|
|
void CEditElement::StreamOut( IStreamOut *pOut ){
|
|
pOut->WriteInt( GetElementType() );
|
|
pOut->WriteInt( m_tagType );
|
|
pOut->WriteZString( m_pTagData );
|
|
}
|
|
|
|
XP_Bool CEditElement::ShouldStreamSelf( CEditSelection& local, CEditSelection& selection)
|
|
{
|
|
return ( local.EqualRange( selection ) || ! local.Contains(selection));
|
|
}
|
|
|
|
// Partially stream out this element and each child.except if we are told to stream all
|
|
void CEditElement::PartialStreamOut( IStreamOut* pOut, CEditSelection& selection)
|
|
{
|
|
CEditSelection local;
|
|
GetAll(local);
|
|
#ifdef DEBUG
|
|
TagType type = GetType();
|
|
#endif
|
|
|
|
if ( local.Intersects(selection) )
|
|
{
|
|
XP_Bool bWriteSelf = ShouldStreamSelf(local, selection);
|
|
if( bWriteSelf )
|
|
{
|
|
StreamOut(pOut);
|
|
}
|
|
CEditElement* pChild;
|
|
for ( pChild = GetChild(); pChild; pChild = pChild->GetNextSibling() )
|
|
{
|
|
pChild->PartialStreamOut(pOut, selection);
|
|
}
|
|
if ( bWriteSelf )
|
|
{
|
|
pOut->WriteInt((int32)eElementNone);
|
|
}
|
|
}
|
|
}
|
|
|
|
XP_Bool CEditElement::ClipToMe(CEditSelection& selection, CEditSelection& local) {
|
|
// Returns TRUE if selection intersects with "this".
|
|
GetAll(local);
|
|
return local.ClipTo(selection);
|
|
}
|
|
|
|
void CEditElement::GetAll(CEditSelection& selection) {
|
|
CEditLeafElement* pFirstMostChild = CEditLeafElement::Cast(GetFirstMostChild());
|
|
if ( ! pFirstMostChild ) {
|
|
XP_ASSERT(FALSE);
|
|
return;
|
|
}
|
|
selection.m_start.m_pElement = GetFirstMostChild()->Leaf();
|
|
selection.m_start.m_iPos = 0;
|
|
CEditLeafElement* pLast = GetLastMostChild()->Leaf();
|
|
CEditLeafElement* pNext = pLast->NextLeaf();
|
|
if ( pNext ) {
|
|
selection.m_end.m_pElement = pNext;
|
|
selection.m_end.m_iPos = 0;
|
|
}
|
|
else {
|
|
// edge of document. Can't select any further.
|
|
selection.m_end.m_pElement = pLast;
|
|
selection.m_end.m_iPos = pLast->GetLen();
|
|
}
|
|
selection.m_bFromStart = FALSE;
|
|
}
|
|
|
|
EEditElementType CEditElement::GetElementType()
|
|
{
|
|
return eElement;
|
|
}
|
|
|
|
// Get parent table of the element
|
|
CEditTableElement* CEditElement::GetParentTable()
|
|
{
|
|
CEditElement *pElement = this;
|
|
do { pElement = pElement->GetParent(); }
|
|
while( pElement && !pElement->IsTable() );
|
|
return (CEditTableElement*)pElement;
|
|
}
|
|
|
|
// Get parent table cell of the element
|
|
CEditTableCellElement* CEditElement::GetParentTableCell()
|
|
{
|
|
CEditElement *pElement = this;
|
|
do { pElement = pElement->GetParent(); }
|
|
while( pElement && !pElement->IsTableCell() );
|
|
return (CEditTableCellElement*)pElement;
|
|
}
|
|
|
|
//
|
|
// static function calls the appropriate stream constructor
|
|
//
|
|
|
|
CEditElement* CEditElement::StreamCtor( IStreamIn *pIn, CEditBuffer *pBuffer ){
|
|
CEditElement* pResult = StreamCtorNoChildren(pIn, pBuffer);
|
|
if ( pResult ) {
|
|
pResult->StreamInChildren(pIn, pBuffer);
|
|
}
|
|
return pResult;
|
|
}
|
|
|
|
void CEditElement::StreamInChildren(IStreamIn* pIn, CEditBuffer* pBuffer){
|
|
CEditElement* pChild;
|
|
while ( (pChild = CEditElement::StreamCtor(pIn, pBuffer)) != NULL ) {
|
|
pChild->InsertAsLastChild(this);
|
|
}
|
|
}
|
|
|
|
CEditElement* CEditElement::StreamCtorNoChildren( IStreamIn *pIn, CEditBuffer *pBuffer ){
|
|
|
|
EEditElementType eType = (EEditElementType) pIn->ReadInt();
|
|
switch( eType ){
|
|
case eElementNone:
|
|
return 0;
|
|
|
|
case eElement:
|
|
return new CEditElement( pIn, pBuffer );
|
|
|
|
case eTextElement:
|
|
return new CEditTextElement( pIn, pBuffer );
|
|
|
|
case eImageElement:
|
|
return new CEditImageElement( pIn, pBuffer );
|
|
|
|
case eHorizRuleElement:
|
|
return new CEditHorizRuleElement( pIn, pBuffer );
|
|
|
|
case eBreakElement:
|
|
return new CEditBreakElement( pIn, pBuffer );
|
|
|
|
case eContainerElement:
|
|
return new CEditContainerElement( pIn, pBuffer );
|
|
|
|
case eListElement:
|
|
return new CEditListElement( pIn, pBuffer );
|
|
|
|
case eIconElement:
|
|
return new CEditIconElement( pIn, pBuffer );
|
|
|
|
case eTargetElement:
|
|
return new CEditTargetElement( pIn, pBuffer );
|
|
|
|
case eTableElement:
|
|
return new CEditTableElement( pIn, pBuffer );
|
|
|
|
case eCaptionElement:
|
|
return new CEditCaptionElement( pIn, pBuffer );
|
|
|
|
case eTableRowElement:
|
|
return new CEditTableRowElement( pIn, pBuffer );
|
|
|
|
case eTableCellElement:
|
|
return new CEditTableCellElement( pIn, pBuffer );
|
|
|
|
case eLayerElement:
|
|
return new CEditLayerElement( pIn, pBuffer );
|
|
|
|
case eDivisionElement:
|
|
return new CEditDivisionElement( pIn, pBuffer );
|
|
|
|
default:
|
|
XP_ASSERT(0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
//
|
|
// Scan up the tree looking to see if we are within 'tagType'. If we stop and
|
|
// we are not at the top of the tree, we found the tag we are looking for.
|
|
//
|
|
XP_Bool CEditElement::Within( int tagType ){
|
|
CEditElement* pParent = GetParent();
|
|
while( pParent && pParent->GetType() != tagType ){
|
|
pParent = pParent->GetParent();
|
|
}
|
|
return (pParent != 0);
|
|
}
|
|
|
|
CEditBuffer* CEditElement::GetEditBuffer(){
|
|
CEditRootDocElement *pE = GetRootDoc();
|
|
if( pE ){
|
|
return pE->GetBuffer();
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
XP_Bool CEditElement::InFormattedText(){
|
|
CEditElement* pParent = GetParent();
|
|
|
|
#ifdef USE_SCRIPT
|
|
if( IsA( P_TEXT) && (Text()->m_tf & (TF_SERVER|TF_SCRIPT|TF_STYLE)) != 0 ){
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
while( pParent && BitSet( edt_setFormattedText, pParent->GetType() ) == 0 ){
|
|
pParent = pParent->GetParent();
|
|
}
|
|
return (pParent != 0);
|
|
}
|
|
|
|
|
|
//
|
|
// Fills in the data member of the tag, as well as the type informaiton.
|
|
//
|
|
void CEditElement::SetTagData( PA_Tag* pTag, char* pTagData){
|
|
PA_Block buff;
|
|
char *locked_buff;
|
|
int iLen;
|
|
|
|
if ( NULL == pTagData ) {
|
|
XP_ASSERT(FALSE);
|
|
return;
|
|
}
|
|
|
|
pTag->type = m_tagType;
|
|
pTag->edit_element = this;
|
|
|
|
iLen = XP_STRLEN(pTagData);
|
|
buff = (PA_Block)PA_ALLOC((iLen+1) * sizeof(char));
|
|
if (buff != NULL)
|
|
{
|
|
PA_LOCK(locked_buff, char *, buff);
|
|
XP_BCOPY(pTagData, locked_buff, iLen);
|
|
locked_buff[iLen] = '\0';
|
|
PA_UNLOCK(buff);
|
|
}
|
|
else {
|
|
// LTNOTE: out of memory, should throw an exception
|
|
return;
|
|
}
|
|
|
|
pTag->data = buff;
|
|
pTag->data_len = iLen;
|
|
pTag->next = NULL;
|
|
return;
|
|
}
|
|
|
|
PA_Tag* CEditElement::TagOpen( int /* iEditOffset */ ){
|
|
PA_Tag *pTag = XP_NEW( PA_Tag );
|
|
XP_BZERO( pTag, sizeof( PA_Tag ) );
|
|
if( GetTagData() ){
|
|
SetTagData( pTag, GetTagData() );
|
|
}
|
|
else {
|
|
SetTagData( pTag, ">" );
|
|
}
|
|
return pTag;
|
|
}
|
|
|
|
PA_Tag* CEditElement::TagEnd( ){
|
|
if( TagHasClose( m_tagType ) || BitSet( edt_setWriteEndTag, m_tagType ) ){
|
|
PA_Tag *pTag = XP_NEW( PA_Tag );
|
|
XP_BZERO( pTag, sizeof( PA_Tag ) );
|
|
pTag->type = m_tagType;
|
|
pTag->is_end = TRUE;
|
|
pTag->edit_element = this;
|
|
return pTag;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
XP_Bool CEditElement::Reduce( CEditBuffer* /* pBuffer */ ){
|
|
if( !BitSet( edt_setSoloTags, GetType() ) && GetChild() == 0 ){
|
|
return TRUE;
|
|
}
|
|
else if( BitSet( edt_setCharFormat, GetType() ) ){
|
|
CEditElement *pNext = GetNextSibling();
|
|
if( pNext && pNext->GetType() == GetType() ){
|
|
// FONTs and Anchors need better compares than this.
|
|
Merge(pNext);
|
|
|
|
// make sure it stays in the tree so it dies a natural death (because
|
|
// it has no children)
|
|
if( pNext )
|
|
pNext->InsertAfter(this);
|
|
return FALSE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
int CEditElement::GetDefaultFontSize(){
|
|
TagType t = GetType();
|
|
if( !BitSet( edt_setTextContainer, t ) ){
|
|
CEditElement* pCont = FindContainer();
|
|
if( pCont ){
|
|
t = pCont->GetType();
|
|
}
|
|
else {
|
|
return 0; // no default font size
|
|
}
|
|
}
|
|
|
|
switch( t ){
|
|
case P_HEADER_1:
|
|
return 6;
|
|
case P_HEADER_2:
|
|
return 5;
|
|
case P_HEADER_3:
|
|
return 4;
|
|
case P_HEADER_4:
|
|
return 3;
|
|
case P_HEADER_5:
|
|
return 2;
|
|
case P_HEADER_6:
|
|
return 1;
|
|
default:
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
CEditInsertPoint CEditElement::IndexToInsertPoint(ElementIndex index, XP_Bool bStickyAfter) {
|
|
if ( index < 0 ) {
|
|
XP_ASSERT(FALSE);
|
|
index = 0;
|
|
}
|
|
CEditElement* pChild;
|
|
CEditElement* pLastChild = NULL;
|
|
ElementIndex childCount = 0;
|
|
// XP_TRACE(("IndexToInsertPoint: 0x%08x (%d) index = %d", this, GetElementIndex(), index));
|
|
for ( pChild = GetChild();
|
|
pChild;
|
|
pChild = pChild->GetNextSibling()) {
|
|
pLastChild = pChild;
|
|
childCount = pChild->GetPersistentCount();
|
|
if ( index < childCount
|
|
|| (index == childCount && ! bStickyAfter) ){
|
|
return pChild->IndexToInsertPoint(index, bStickyAfter);
|
|
}
|
|
|
|
index -= childCount;
|
|
}
|
|
if ( ! pLastChild ) { // No children at all
|
|
childCount = GetPersistentCount();
|
|
if ( index > childCount ){
|
|
XP_ASSERT(FALSE);
|
|
index = childCount;
|
|
}
|
|
return CEditInsertPoint(this, index);
|
|
}
|
|
// Ran off end of children
|
|
return pLastChild->IndexToInsertPoint(childCount, bStickyAfter);
|
|
}
|
|
|
|
CPersistentEditInsertPoint CEditElement::GetPersistentInsertPoint(ElementOffset offset){
|
|
XP_ASSERT(FALSE); // This method should never be called
|
|
return CPersistentEditInsertPoint(GetElementIndex() + offset);
|
|
}
|
|
|
|
ElementIndex CEditElement::GetPersistentCount()
|
|
{
|
|
ElementIndex count = 0;
|
|
for ( CEditElement* c = GetChild();
|
|
c;
|
|
c = c->GetNextSibling() ) {
|
|
count += c->GetPersistentCount();
|
|
}
|
|
return count;
|
|
}
|
|
|
|
ElementIndex CEditElement::GetElementIndex()
|
|
{
|
|
CEditElement* parent = GetParent();
|
|
if ( parent )
|
|
return parent->GetElementIndexOf(this);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
ElementIndex CEditElement::GetElementIndexOf(CEditElement* child)
|
|
{
|
|
ElementIndex index = GetElementIndex();
|
|
for ( CEditElement* c = GetChild();
|
|
c;
|
|
c = c->GetNextSibling() ) {
|
|
if ( child == c )
|
|
{
|
|
return index;
|
|
}
|
|
index += c->GetPersistentCount();
|
|
}
|
|
XP_ASSERT(FALSE); // Couldn't find this child.
|
|
return index;
|
|
}
|
|
|
|
void CEditElement::FinishedLoad( CEditBuffer* pBuffer ){
|
|
CEditElement* pNext = 0;
|
|
for ( CEditElement* pChild = GetChild();
|
|
pChild;
|
|
pChild = pNext ) {
|
|
pNext = pChild->GetNextSibling();
|
|
if ( IsAcceptableChild(*pChild) ){
|
|
pChild->FinishedLoad(pBuffer);
|
|
}
|
|
else {
|
|
#ifdef DEBUG
|
|
XP_TRACE(("Removing an unacceptable child. Parent type %d child type %d.\n",
|
|
GetElementType(), pChild->GetElementType()));
|
|
#endif
|
|
delete pChild;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Containers can't be deleted during adjustment or we will blow up. We need
|
|
// While adjusting containers, new containers are inserted.
|
|
//
|
|
void CEditElement::AdjustContainers( CEditBuffer* pBuffer ){
|
|
for ( CEditElement* pChild = GetChild();
|
|
pChild;
|
|
pChild = pChild->GetNextSibling() ) {
|
|
|
|
pChild->AdjustContainers(pBuffer);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int16 CEditElement::GetWinCSID(){
|
|
// International Character set ID
|
|
CEditRootDocElement* pRoot = GetRootDoc();
|
|
if ( pRoot ) {
|
|
CEditBuffer* pBuffer = pRoot->GetEditBuffer();
|
|
if ( pBuffer ) {
|
|
return pBuffer->GetRAMCharSetID();
|
|
}
|
|
}
|
|
XP_ASSERT(FALSE);
|
|
return CS_FE_ASCII;
|
|
}
|
|
|
|
void CEditElement::EnsureSelectableSiblings(CEditBuffer* pBuffer)
|
|
{
|
|
// To ensure that we can edit before or after the table,
|
|
// make sure that there is a container before and after the table.
|
|
|
|
CEditElement* pParent = GetParent();
|
|
if ( ! pParent ) {
|
|
return;
|
|
}
|
|
{
|
|
// Make sure the previous sibling exists and is a container
|
|
CEditElement* pPrevious = GetPreviousSibling();
|
|
// EXPERIMENTAL: Don't force another container before inserted table
|
|
if (pPrevious && pPrevious->GetType() == P_DIVISION)
|
|
pPrevious = pPrevious->GetChild();
|
|
#if 0
|
|
if ( ! pPrevious ) {
|
|
#else
|
|
if ( ! pPrevious || !pPrevious->IsContainer() ) {
|
|
#endif
|
|
pPrevious = CEditContainerElement::NewDefaultContainer( NULL,
|
|
pParent->GetDefaultAlignment() );
|
|
pPrevious->InsertBefore(this);
|
|
pPrevious->FinishedLoad(pBuffer);
|
|
}
|
|
}
|
|
{
|
|
// Make sure the next sibling exists and is container
|
|
CEditElement* pNext = GetNextSibling();
|
|
if (pNext && pNext->GetType() == P_DIVISION)
|
|
pNext = pNext->GetChild();
|
|
#if 0
|
|
if ( ! pNext || pNext->IsEndContainer() ) {
|
|
#else
|
|
if ( ! pNext || pNext->IsEndContainer() || !pNext->IsContainer()) {
|
|
#endif
|
|
pNext = CEditContainerElement::NewDefaultContainer( NULL,
|
|
pParent->GetDefaultAlignment() );
|
|
pNext->InsertAfter(this);
|
|
pNext->FinishedLoad(pBuffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Reverse navagation (left)
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// these routines are a little expensive. If we need to, we can make the linked
|
|
// lists of elements, doubly linked.
|
|
//
|
|
CEditElement* CEditElement::GetPreviousSibling(){
|
|
if( GetParent() == 0 ){
|
|
return 0;
|
|
}
|
|
|
|
// point to our first sibling.
|
|
CEditElement *pSibling = GetParent()->GetChild();
|
|
|
|
// if we are the first sibling, then there is no previous sibling.
|
|
if ( pSibling == this ){
|
|
return 0;
|
|
}
|
|
|
|
// if we get an Exception in this loop, the tree is messed up!
|
|
while( pSibling->GetNextSibling() != this ){
|
|
pSibling = pSibling->GetNextSibling();
|
|
}
|
|
return pSibling;
|
|
|
|
}
|
|
|
|
void CEditElement::SetChild(CEditElement *pChild){
|
|
RawSetChild(pChild);
|
|
}
|
|
|
|
void CEditElement::SetNextSibling(CEditElement* pNext){
|
|
RawSetNextSibling(pNext);
|
|
}
|
|
|
|
CEditElement* CEditElement::GetLastChild(){
|
|
CEditElement* pChild;
|
|
if( (pChild = GetChild()) == 0 ){
|
|
return 0;
|
|
}
|
|
while( pChild->GetNextSibling() ){
|
|
pChild = pChild->GetNextSibling();
|
|
}
|
|
return pChild;
|
|
}
|
|
|
|
CEditElement* CEditElement::GetFirstMostChild(){
|
|
CEditElement* pChild = this;
|
|
CEditElement* pPrev = pChild;
|
|
while( pPrev ){
|
|
pChild = pPrev;
|
|
pPrev = pPrev->GetChild();
|
|
}
|
|
return pChild;
|
|
}
|
|
|
|
CEditElement* CEditElement::GetLastMostChild(){
|
|
CEditElement* pChild = this;
|
|
CEditElement* pNext = pChild;
|
|
while( pNext ){
|
|
pChild = pNext;
|
|
pNext = pNext->GetLastChild();
|
|
}
|
|
return pChild;
|
|
}
|
|
|
|
CEditContainerElement* CEditElement::GetPreviousNonEmptyContainer()
|
|
{
|
|
CEditElement *pPrev = GetPreviousSibling();
|
|
CEditContainerElement* pPrevContainer;
|
|
while (pPrev && pPrev->IsContainer())
|
|
{
|
|
pPrevContainer=pPrev->Container();
|
|
if (!pPrevContainer->IsEmpty())
|
|
return pPrevContainer;
|
|
pPrev=pPrev->GetPreviousSibling();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
CEditContainerElement* CEditElement::GetNextNonEmptyContainer()
|
|
{
|
|
CEditElement *pNext = GetNextSibling();
|
|
CEditContainerElement* pNextContainer;
|
|
while (pNext&& pNext->IsContainer())
|
|
{
|
|
pNextContainer=pNext->Container();
|
|
if (!pNextContainer->IsEmpty())
|
|
return pNextContainer;
|
|
pNext=pNext->GetNextSibling();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
/////END CEDITELEMENT IMPLEMENTATION///////////
|
|
///////////////////////////////////////////////
|
|
|
|
|
|
CEditTableCellElement* CEditElement::GetTableCell() {
|
|
// Returns containing cell, or NULL if none.
|
|
CEditElement* pElement = this;
|
|
while ( pElement ) {
|
|
if ( pElement->IsTableCell() ) {
|
|
break;
|
|
}
|
|
if ( pElement->IsSubDoc() && pElement != this ) {
|
|
return NULL;
|
|
}
|
|
pElement = pElement->GetParent();
|
|
}
|
|
return (CEditTableCellElement*) pElement;
|
|
}
|
|
|
|
CEditTableCellElement* CEditElement::GetTableCellIgnoreSubdoc() {
|
|
// Returns containing cell, or NULL if none.
|
|
CEditElement* pElement = this;
|
|
while ( pElement ) {
|
|
if ( pElement->IsTableCell() ) {
|
|
break;
|
|
}
|
|
pElement = pElement->GetParent();
|
|
}
|
|
return (CEditTableCellElement*) pElement;
|
|
}
|
|
|
|
CEditTableRowElement* CEditElement::GetTableRow() {
|
|
// Returns containing cell, or NULL if none.
|
|
CEditElement* pElement = this;
|
|
while ( pElement ) {
|
|
if ( pElement->IsTableRow() ) {
|
|
break;
|
|
}
|
|
if ( pElement->IsSubDoc() && pElement != this ) {
|
|
return NULL;
|
|
}
|
|
pElement = pElement->GetParent();
|
|
}
|
|
return (CEditTableRowElement*) pElement;
|
|
}
|
|
|
|
CEditTableRowElement* CEditElement::GetTableRowIgnoreSubdoc() {
|
|
// Returns containing cell, or NULL if none.
|
|
CEditElement* pElement = this;
|
|
while ( pElement ) {
|
|
if ( pElement->IsTableRow() ) {
|
|
break;
|
|
}
|
|
pElement = pElement->GetParent();
|
|
}
|
|
return (CEditTableRowElement*) pElement;
|
|
}
|
|
|
|
CEditCaptionElement* CEditElement::GetCaption() {
|
|
// Returns containing tavle, or NULL if none.
|
|
CEditElement* pElement = this;
|
|
while ( pElement ) {
|
|
if ( pElement->IsCaption() ) {
|
|
break;
|
|
}
|
|
if ( pElement->IsSubDoc() && pElement != this ) {
|
|
return NULL;
|
|
}
|
|
pElement = pElement->GetParent();
|
|
}
|
|
return (CEditCaptionElement*) pElement;
|
|
}
|
|
|
|
CEditCaptionElement* CEditElement::GetCaptionIgnoreSubdoc() {
|
|
// Returns containing table, or NULL if none.
|
|
CEditElement* pElement = this;
|
|
while ( pElement ) {
|
|
if ( pElement->IsCaption() ) {
|
|
break;
|
|
}
|
|
pElement = pElement->GetParent();
|
|
}
|
|
return (CEditCaptionElement*) pElement;
|
|
}
|
|
|
|
CEditTableElement* CEditElement::GetTable() {
|
|
// Returns containing table, or NULL if none.
|
|
CEditElement* pElement = this;
|
|
while ( pElement ) {
|
|
if ( pElement->IsTable() ) {
|
|
break;
|
|
}
|
|
if ( pElement->IsSubDoc() && pElement != this ) {
|
|
return NULL;
|
|
}
|
|
pElement = pElement->GetParent();
|
|
}
|
|
return (CEditTableElement*) pElement;
|
|
}
|
|
|
|
CEditTableElement* CEditElement::GetTableIgnoreSubdoc() {
|
|
// Returns containing table, or NULL if none.
|
|
CEditElement* pElement = this;
|
|
while ( pElement ) {
|
|
if ( pElement->IsTable() ) {
|
|
break;
|
|
}
|
|
pElement = pElement->GetParent();
|
|
}
|
|
return (CEditTableElement*) pElement;
|
|
}
|
|
|
|
CEditElement* CEditElement::GetTopmostTableOrLayer() {
|
|
// Returns containing table, or NULL if none.
|
|
CEditElement* pResult = NULL;
|
|
CEditElement* pElement = this;
|
|
while ( pElement ) {
|
|
if ( pElement->IsTable() || pElement->IsLayer()) {
|
|
pResult = pElement;
|
|
}
|
|
pElement = pElement->GetParent();
|
|
}
|
|
return pResult;
|
|
}
|
|
|
|
CEditElement* CEditElement::GetTableOrLayerIgnoreSubdoc() {
|
|
// Returns containing table, or NULL if none.
|
|
CEditElement* pElement = this;
|
|
while ( pElement ) {
|
|
if ( pElement->IsTable() || pElement->IsLayer() ) {
|
|
break;
|
|
}
|
|
pElement = pElement->GetParent();
|
|
}
|
|
return pElement;
|
|
}
|
|
|
|
CEditElement* CEditElement::GetSubDocOrLayerSkipRoot() {
|
|
// Returns containing sub-doc, or NULL if none.
|
|
CEditElement* pElement = this;
|
|
while ( pElement ) {
|
|
if ( pElement->IsLayer() ||
|
|
(pElement->IsSubDoc() && !pElement->IsRoot() ) ) {
|
|
break;
|
|
}
|
|
pElement = pElement->GetParent();
|
|
}
|
|
return pElement;
|
|
}
|
|
|
|
CEditLayerElement* CEditElement::GetLayer() {
|
|
// Returns containing Layer, or NULL if none.
|
|
CEditElement* pElement = this;
|
|
while ( pElement ) {
|
|
if ( pElement->IsLayer() ) {
|
|
break;
|
|
}
|
|
if ( pElement->IsSubDoc() && pElement != this ) {
|
|
return NULL;
|
|
}
|
|
pElement = pElement->GetParent();
|
|
}
|
|
return (CEditLayerElement*) pElement;
|
|
}
|
|
|
|
CEditLayerElement* CEditElement::GetLayerIgnoreSubdoc() {
|
|
// Returns containing Layer, or NULL if none.
|
|
CEditElement* pElement = this;
|
|
while ( pElement ) {
|
|
if ( pElement->IsLayer() ) {
|
|
break;
|
|
}
|
|
pElement = pElement->GetParent();
|
|
}
|
|
return (CEditLayerElement*) pElement;
|
|
}
|
|
|
|
CEditSubDocElement* CEditElement::GetSubDoc() {
|
|
// Returns containing sub-doc, or NULL if none.
|
|
CEditElement* pElement = this;
|
|
while ( pElement ) {
|
|
if ( pElement->IsSubDoc() ) {
|
|
break;
|
|
}
|
|
pElement = pElement->GetParent();
|
|
}
|
|
return (CEditSubDocElement*) pElement;
|
|
}
|
|
|
|
CEditSubDocElement* CEditElement::GetSubDocSkipRoot() {
|
|
// Returns containing sub-doc, or NULL if none.
|
|
CEditElement* pElement = this;
|
|
while ( pElement ) {
|
|
if ( pElement->IsSubDoc() && !pElement->IsRoot() ) {
|
|
break;
|
|
}
|
|
pElement = pElement->GetParent();
|
|
}
|
|
return (CEditSubDocElement*) pElement;
|
|
}
|
|
|
|
|
|
CEditRootDocElement* CEditElement::GetRootDoc(){
|
|
// Returns containing root.
|
|
CEditElement* pElement = this;
|
|
while ( pElement ) {
|
|
if ( pElement->IsRoot() ) {
|
|
break;
|
|
}
|
|
pElement = pElement->GetParent();
|
|
}
|
|
return (CEditRootDocElement*) pElement;
|
|
}
|
|
|
|
XP_Bool CEditElement::InMungableMailQuote(){
|
|
// Returns true if this element is within a mungable mail quote.
|
|
CEditElement* pElement = this;
|
|
while ( pElement ) {
|
|
if ( pElement->IsSubDoc() ) {
|
|
return FALSE;
|
|
}
|
|
else if ( pElement->IsList() ) {
|
|
CEditListElement* pList = pElement->List();
|
|
if ( pList->IsMailQuote() ) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
pElement = pElement->GetParent();
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
XP_Bool CEditElement::InMailQuote(){
|
|
// Returns true if this element is within a mail quote.
|
|
return (GetMailQuote() != NULL);
|
|
}
|
|
|
|
CEditListElement* CEditElement::GetMailQuote() {
|
|
CEditElement* pElement = this;
|
|
while ( pElement ) {
|
|
if ( pElement->IsList() ) {
|
|
CEditListElement* pList = pElement->List();
|
|
if ( pList->IsMailQuote() ) {
|
|
return pList;
|
|
}
|
|
}
|
|
pElement = pElement->GetParent();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// This is silly -- we never return anything other than ED_ALIGN_DEFAULT???
|
|
ED_Alignment CEditElement::GetDefaultAlignment(){
|
|
if ( m_pParent )
|
|
return m_pParent->GetDefaultAlignment();
|
|
|
|
return ED_ALIGN_DEFAULT;
|
|
}
|
|
|
|
ED_Alignment CEditElement::GetDefaultVAlignment(){
|
|
if ( m_pParent )
|
|
return m_pParent->GetDefaultVAlignment();
|
|
return ED_ALIGN_TOP;
|
|
}
|
|
|
|
CEditElement* CEditElement::UpLeft( PMF_EditElementTest pmf, void *pTestData ){
|
|
CEditElement *pPrev = this;
|
|
CEditElement* pRet;
|
|
|
|
while( (pPrev = pPrev->GetPreviousSibling()) != NULL ){
|
|
pRet = pPrev->DownLeft( pmf, pTestData );
|
|
if( pRet ){
|
|
return pRet;
|
|
}
|
|
}
|
|
if( GetParent() ){
|
|
return GetParent()->UpLeft( pmf, pTestData );
|
|
}
|
|
else{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// All the children come before the node.
|
|
//
|
|
CEditElement* CEditElement::DownLeft( PMF_EditElementTest pmf, void *pTestData,
|
|
XP_Bool /* bIgnoreThis */ ){
|
|
CEditElement *pChild;
|
|
CEditElement *pRet;
|
|
|
|
pChild = GetLastChild();
|
|
while( pChild != NULL ){
|
|
if( (pRet = pChild->DownLeft( pmf, pTestData )) != NULL ){
|
|
return pRet;
|
|
}
|
|
pChild = pChild->GetPreviousSibling();
|
|
}
|
|
if( TestIsTrue( pmf, pTestData ) ){
|
|
return this;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
CEditElement* CEditElement::FindPreviousElement( PMF_EditElementTest pmf,
|
|
void *pTestData ){
|
|
return UpLeft( pmf, pTestData );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// forward navagation (right)
|
|
//-----------------------------------------------------------------------------
|
|
CEditElement* CEditElement::UpRight( PMF_EditElementTest pmf, void *pTestData ){
|
|
CEditElement *pNext = this;
|
|
CEditElement* pRet;
|
|
|
|
while( (pNext = pNext->GetNextSibling()) != NULL ){
|
|
pRet = pNext->DownRight( pmf, pTestData );
|
|
if( pRet ){
|
|
return pRet;
|
|
}
|
|
}
|
|
if( GetParent() ){
|
|
return GetParent()->UpRight( pmf, pTestData );
|
|
}
|
|
else{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
CEditElement* CEditElement::DownRight( PMF_EditElementTest pmf, void *pTestData,
|
|
XP_Bool bIgnoreThis ){
|
|
|
|
CEditElement *pChild;
|
|
CEditElement *pRet;
|
|
|
|
if( !bIgnoreThis && TestIsTrue( pmf, pTestData ) ){
|
|
return this;
|
|
}
|
|
pChild = GetChild();
|
|
while( pChild != NULL ){
|
|
if( (pRet = pChild->DownRight( pmf, pTestData )) != NULL ){
|
|
return pRet;
|
|
}
|
|
pChild = pChild->GetNextSibling();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
CEditElement* CEditElement::FindNextElement( PMF_EditElementTest pmf,
|
|
void *pTestData ){
|
|
CEditElement *pRet;
|
|
|
|
pRet = DownRight( pmf, pTestData, TRUE );
|
|
if( pRet ){
|
|
return pRet;
|
|
}
|
|
return UpRight( pmf, pTestData );
|
|
}
|
|
|
|
|
|
//
|
|
// routine looks for a valid text block for positioning during editing.
|
|
//
|
|
XP_Bool CEditElement::FindText( void* /*pVoid*/ ){
|
|
CEditElement *pPrev;
|
|
|
|
//
|
|
// if this is a text block and the layout element actually points to
|
|
// something, return it.
|
|
//
|
|
if( GetType() == P_TEXT ){
|
|
CEditTextElement *pText = Text();
|
|
if( pText->GetLen() == 0 ){
|
|
//
|
|
// Find only empty text blocks that occur at the beginning of
|
|
// a paragraph (as a paragraph place holder)
|
|
//
|
|
pPrev = FindPreviousElement( &CEditElement::FindTextAll, 0 );
|
|
if( pPrev && pPrev->FindContainer() == FindContainer() ){
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
XP_Bool CEditElement::FindImage( void* /*pVoid*/ ){
|
|
return IsImage() ;
|
|
}
|
|
|
|
XP_Bool CEditElement::FindTarget( void* /*pVoid*/ ){
|
|
return GetElementType() == eTargetElement ;
|
|
}
|
|
|
|
XP_Bool CEditElement::FindUnknownHTML( void* /*pVoid*/ ){
|
|
return IsLeaf() && Leaf()->IsUnknownHTML();
|
|
}
|
|
|
|
//
|
|
// routine looks for a valid text block for positioning during editing.
|
|
//
|
|
XP_Bool CEditElement::FindLeaf( void* pVoid ){
|
|
if( !IsLeaf() ){
|
|
return FALSE;
|
|
}
|
|
if( IsA(P_TEXT) ){
|
|
return FindText( pVoid );
|
|
}
|
|
else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
XP_Bool CEditElement::FindTextAll( void* /*pVoid*/ ){
|
|
|
|
//
|
|
// if this is a text block and the layout element actually points to
|
|
// something, return it.
|
|
//
|
|
if( GetType() == P_TEXT ){
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
XP_Bool CEditElement::FindLeafAll( void* /*pVoid*/ ){
|
|
|
|
//
|
|
// if this is a text block and the layout element actually points to
|
|
// something, return it.
|
|
//
|
|
if( IsLeaf() ){
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
XP_Bool CEditElement::FindTable( void* /*pVoid*/ ){
|
|
return IsTable();
|
|
}
|
|
|
|
XP_Bool CEditElement::FindTableRow( void* /*pVoid*/ ){
|
|
return IsTableRow();
|
|
}
|
|
|
|
XP_Bool CEditElement::FindTableCell( void* /*pVoid*/ ){
|
|
return IsTableCell();
|
|
}
|
|
|
|
XP_Bool CEditElement::SplitContainerTest( void* /*pVoid*/ ){
|
|
return BitSet( edt_setTextContainer, GetType() );
|
|
}
|
|
|
|
XP_Bool CEditElement::SplitFormattingTest( void* pVoid ){
|
|
return (void*)GetType() == pVoid;
|
|
}
|
|
|
|
XP_Bool CEditElement::FindContainer( void* /*pVoid*/ ){
|
|
return IsContainer();
|
|
}
|
|
|
|
XP_Bool CEditElement::GetWidth(XP_Bool * pPercent, int32 * pWidth) {
|
|
PA_Tag *pTag = TagOpen(0);
|
|
if( !pTag ){
|
|
return FALSE;
|
|
}
|
|
XP_Bool bPercent;
|
|
int32 iWidth;
|
|
XP_Bool bDefined = edt_FetchDimension( pTag,
|
|
PARAM_WIDTH, &iWidth, &bPercent, 0, FALSE, GetWinCSID() );
|
|
if( bDefined ){
|
|
if( pPercent ){
|
|
*pPercent = bPercent;
|
|
}
|
|
if( pWidth ){
|
|
*pWidth = iWidth;
|
|
}
|
|
}
|
|
return bDefined;
|
|
}
|
|
|
|
XP_Bool CEditElement::GetHeight(XP_Bool * pPercent, int32 * pHeight) {
|
|
PA_Tag *pTag = TagOpen(0);
|
|
if( !pTag ){
|
|
return FALSE;
|
|
}
|
|
XP_Bool bPercent;
|
|
int32 iHeight;
|
|
XP_Bool bDefined = edt_FetchDimension( pTag,
|
|
PARAM_HEIGHT, &iHeight, &bPercent, 0, FALSE, GetWinCSID() );
|
|
if( bDefined ){
|
|
if( pPercent ){
|
|
*pPercent = bPercent;
|
|
}
|
|
if( pHeight ){
|
|
*pHeight = iHeight;
|
|
}
|
|
}
|
|
return bDefined;
|
|
}
|
|
|
|
void CEditElement::SetSize(XP_Bool /* bWidthPercent */, int32 /* iWidth */,
|
|
XP_Bool /* bHeightPercent */, int32 /* iHeight */){
|
|
}
|
|
|
|
XP_Bool CEditElement::CanReflow() {
|
|
return TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Default printing routines.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void CEditElement::PrintOpen( CPrintState *pPrintState ){
|
|
InternalPrintOpen(pPrintState, m_pTagData);
|
|
}
|
|
|
|
void CEditElement::InternalPrintOpen( CPrintState *pPrintState, char* pTagData ){
|
|
if( !(BitSet( edt_setCharFormat, GetType())
|
|
|| BitSet( edt_setSuppressNewlineBefore, GetType())) ){
|
|
pPrintState->m_pOut->Write( "\n", 1 );
|
|
pPrintState->m_iCharPos = 0;
|
|
}
|
|
|
|
if( pTagData && *pTagData != '>' ){
|
|
char *pStr = pTagData;
|
|
while( *pStr == ' ' ) pStr++;
|
|
// Trim trailing white-space in-place
|
|
{
|
|
intn len = XP_STRLEN(pStr);
|
|
while ( len > 1 && pStr[len-2] == ' ') {
|
|
len--;
|
|
}
|
|
if ( len > 1 ) {
|
|
pStr[len-1] = '>';
|
|
pStr[len] = '\0';
|
|
}
|
|
}
|
|
|
|
pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "<%s %s",
|
|
EDT_TagString(GetType()),pStr);
|
|
}
|
|
else {
|
|
pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "<%s>",
|
|
EDT_TagString(GetType()) );
|
|
}
|
|
|
|
if ( BitSet( edt_setRequireNewlineAfter, GetType()) ){
|
|
pPrintState->m_pOut->Write( "\n", 1 );
|
|
pPrintState->m_iCharPos = 0;
|
|
}
|
|
}
|
|
|
|
void CEditElement::PrintEnd( CPrintState *pPrintState ){
|
|
if( TagHasClose( GetType() ) || BitSet( edt_setWriteEndTag, GetType() ) ){
|
|
pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "</%s>",
|
|
EDT_TagString(GetType()) );
|
|
if( !BitSet( edt_setCharFormat, GetType() ) ){
|
|
pPrintState->m_pOut->Write( "\n", 1 );
|
|
pPrintState->m_iCharPos = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Insertion routines
|
|
//-----------------------------------------------------------------------------
|
|
CEditElement* CEditElement::InsertAfter( CEditElement *pPrev ){
|
|
XP_ASSERT(m_pParent == NULL);
|
|
m_pParent = pPrev->GetParent();
|
|
SetNextSibling(pPrev->GetNextSibling());
|
|
pPrev->SetNextSibling(this);
|
|
return m_pParent;
|
|
}
|
|
|
|
CEditElement* CEditElement::InsertBefore( CEditElement *pNext ){
|
|
XP_ASSERT(m_pParent == NULL);
|
|
XP_ASSERT(pNext != NULL);
|
|
CEditElement *pPrev = pNext->GetPreviousSibling();
|
|
if( pPrev == 0 ){
|
|
InsertAsFirstChild( pNext->GetParent() );
|
|
}
|
|
else {
|
|
m_pParent = pNext->GetParent();
|
|
SetNextSibling(pPrev->m_pNext);
|
|
pPrev->SetNextSibling(this);
|
|
}
|
|
return m_pParent;
|
|
}
|
|
|
|
|
|
void CEditElement::InsertAsFirstChild( CEditElement *pParent ){
|
|
XP_ASSERT(m_pParent == NULL);
|
|
m_pParent = pParent;
|
|
SetNextSibling(pParent->GetChild());
|
|
pParent->SetChild(this);
|
|
}
|
|
|
|
void CEditElement::InsertAsLastChild( CEditElement *pParent ){
|
|
XP_ASSERT(m_pParent == NULL);
|
|
m_pParent = pParent;
|
|
SetNextSibling( 0 );
|
|
CEditElement *pPrev = pParent->GetLastChild();
|
|
if( pPrev == 0 ){
|
|
pParent->SetChild(this);
|
|
}
|
|
else {
|
|
pPrev->SetNextSibling(this);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
CEditElement* CEditElement::Split( CEditElement *pLastChild,
|
|
CEditElement* pCloneTree,
|
|
PMF_EditElementTest pmf,
|
|
void *pData ){
|
|
|
|
CEditElement *pClone = Clone();
|
|
pClone->SetChild(pCloneTree);
|
|
|
|
if( pLastChild->m_pNext ){
|
|
if( pCloneTree != 0 ){
|
|
pCloneTree->SetNextSibling(pLastChild->m_pNext);
|
|
}
|
|
else {
|
|
pClone->SetChild(pLastChild->m_pNext);
|
|
}
|
|
pLastChild->SetNextSibling( 0 );
|
|
}
|
|
|
|
//
|
|
// Reparent all the children that have been moved to the clone.
|
|
//
|
|
CEditElement* pNext = pClone->m_pChild;
|
|
while( pNext ){
|
|
pNext->m_pParent = pClone;
|
|
pNext = pNext->GetNextSibling();
|
|
}
|
|
|
|
|
|
//
|
|
// If we are at the container point
|
|
//
|
|
if( TestIsTrue( pmf, pData ) ){
|
|
return pClone->InsertAfter( this );
|
|
}
|
|
else {
|
|
return GetParent()->Split( this, pClone, pmf, pData );
|
|
}
|
|
|
|
}
|
|
|
|
CEditElement* CEditElement::Clone( CEditElement *pParent ){
|
|
return new CEditElement(pParent, GetType(), GetTagData());
|
|
}
|
|
|
|
|
|
//
|
|
// Copied formatting, returns the bottom of the chain (Child most formatting
|
|
// element)
|
|
//
|
|
CEditElement* CEditElement::CloneFormatting( TagType endType ){
|
|
if( GetType() == endType ){
|
|
return 0;
|
|
}
|
|
else {
|
|
return Clone( GetParent()->CloneFormatting(endType) );
|
|
}
|
|
}
|
|
|
|
XP_Bool CEditElement::IsFirstInContainer(){
|
|
CEditElement *pPrevious, *pContainer, *pPreviousContainer;
|
|
|
|
pPrevious = PreviousLeaf();
|
|
if( pPrevious ){
|
|
pContainer = FindContainer();
|
|
pPreviousContainer = pPrevious->FindContainer();
|
|
if( pContainer != pPreviousContainer ){
|
|
return TRUE;
|
|
}
|
|
else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
CEditTextElement* CEditElement::PreviousTextInContainer(){
|
|
CEditElement *pPrevious, *pContainer, *pPreviousContainer;
|
|
pPrevious = FindPreviousElement( &CEditElement::FindText, 0 );
|
|
if( pPrevious ){
|
|
pContainer = FindContainer();
|
|
pPreviousContainer = pPrevious->FindContainer();
|
|
if( pContainer != pPreviousContainer ){
|
|
return 0;
|
|
}
|
|
else {
|
|
return pPrevious->Text();
|
|
}
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
CEditTextElement* CEditElement::TextInContainerAfter(){
|
|
CEditElement *pNext, *pContainer, *pNextContainer;
|
|
|
|
pNext = FindNextElement( &CEditElement::FindText, 0 );
|
|
if( pNext ){
|
|
pContainer = FindContainer();
|
|
pNextContainer = pNext->FindContainer();
|
|
if( pContainer != pNextContainer ){
|
|
return 0;
|
|
}
|
|
else {
|
|
return pNext->Text();
|
|
}
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
CEditLeafElement* CEditElement::PreviousLeafInContainer(){
|
|
CEditElement *pPrevious, *pContainer, *pPreviousContainer;
|
|
pPrevious = PreviousLeaf();
|
|
if( pPrevious ){
|
|
pContainer = FindContainer();
|
|
pPreviousContainer = pPrevious->FindContainer();
|
|
if( pContainer != pPreviousContainer ){
|
|
return 0;
|
|
}
|
|
else {
|
|
return pPrevious->Leaf();
|
|
}
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
CEditLeafElement* CEditElement::LeafInContainerAfter(){
|
|
CEditElement *pNext, *pContainer, *pNextContainer;
|
|
|
|
pNext = NextLeaf();
|
|
if( pNext ){
|
|
pContainer = FindContainer();
|
|
pNextContainer = pNext->FindContainer();
|
|
if( pContainer != pNextContainer ){
|
|
return 0;
|
|
}
|
|
else {
|
|
return pNext->Leaf();
|
|
}
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
CEditLeafElement* CEditElement::NextLeafAll(XP_Bool bForward){
|
|
if ( bForward ) {
|
|
return (CEditLeafElement*) FindNextElement(&CEditElement::FindLeafAll, 0 );
|
|
}
|
|
else {
|
|
return (CEditLeafElement*) FindPreviousElement(&CEditElement::FindLeafAll, 0 );
|
|
}
|
|
}
|
|
|
|
CEditElement* CEditElement::GetRoot(){
|
|
CEditElement* pRoot = this;
|
|
while ( pRoot ) {
|
|
CEditElement* pParent = pRoot->GetParent();
|
|
if ( ! pParent ) break;
|
|
pRoot = pParent;
|
|
}
|
|
return pRoot;
|
|
}
|
|
|
|
CEditElement* CEditElement::GetCommonAncestor(CEditElement* pOther){
|
|
if ( this == pOther ) return this;
|
|
|
|
// Start at root, and trickle down to find where they diverge
|
|
|
|
CEditElement* pRoot = GetRoot();
|
|
CEditElement* pRootOther = pOther->GetRoot();
|
|
if ( pRoot != pRootOther ) return NULL;
|
|
|
|
CEditElement* pCommon = pRoot;
|
|
|
|
while ( pCommon ){
|
|
CEditElement* pAncestor = pCommon->GetChildContaining(this);
|
|
CEditElement* pAncestorOther = pCommon->GetChildContaining(pOther);
|
|
if ( pAncestor != pAncestorOther ) break;
|
|
pCommon = pAncestor;
|
|
}
|
|
return pCommon;
|
|
}
|
|
|
|
CEditElement* CEditElement::GetChildContaining(CEditElement* pDescendant){
|
|
CEditElement* pParent = pDescendant;
|
|
while ( pParent ) {
|
|
CEditElement* pTemp = pParent->GetParent();
|
|
if ( pTemp == this ) break; // Our direct child
|
|
pParent = pTemp;
|
|
}
|
|
return pParent;
|
|
}
|
|
|
|
|
|
XP_Bool CEditElement::IsAcceptableChild(CEditElement& /* pChild */) {return TRUE;}
|
|
|
|
//
|
|
// Unlink from parent, but keep children
|
|
//
|
|
void CEditElement::Unlink(){
|
|
CEditElement* pParent = GetParent();
|
|
if( pParent ){
|
|
CEditElement *pPrev = GetPreviousSibling();
|
|
if( pPrev ){
|
|
pPrev->SetNextSibling( GetNextSibling() );
|
|
}
|
|
else {
|
|
pParent->SetChild(GetNextSibling());
|
|
}
|
|
m_pParent = 0;
|
|
SetNextSibling( 0 );
|
|
}
|
|
|
|
}
|
|
|
|
// LTNOTE: 01/01/96
|
|
// We take take the characteristics of the thing that follows this paragraph
|
|
// instead of keeping this paragraphs characteristics.
|
|
//
|
|
// jhp -- paste takes characteristic of second, but backspace takes
|
|
// characteristic of first, so we need a flag to control it.
|
|
// bCopyAppendAttributes == TRUE for the second (cut/paste)
|
|
// bCopyAppendAttributes == FALSE for the first (backspace/delete)
|
|
|
|
void CEditElement::Merge( CEditElement *pAppendBlock, XP_Bool bCopyAppendAttributes ){
|
|
CEditElement* pChild = GetLastChild();
|
|
CEditElement* pAppendChild = pAppendBlock->GetChild();
|
|
|
|
// LTNOTE: 01/01/96 - I don't think this should be happening anymore.
|
|
// The way we deal with leaves and containers should keep this from
|
|
// occuring...
|
|
//
|
|
// Check to see if pAppendBlock is a child of this.
|
|
//
|
|
// LTNOTE: this is a case where we have
|
|
// UL:
|
|
// text: some text
|
|
// LI:
|
|
// text: XXX some More text
|
|
// LI:
|
|
// text: and some more.
|
|
//
|
|
// Merge occurs before xxx
|
|
//
|
|
CEditElement *pAppendParent = pAppendBlock->GetParent();
|
|
CEditElement *pAppendBlock2 = pAppendBlock;
|
|
XP_Bool bDone = FALSE;
|
|
while( !bDone && pAppendParent ){
|
|
if( pAppendParent == this ){
|
|
pChild = pAppendBlock2->GetPreviousSibling();
|
|
bDone = TRUE;
|
|
}
|
|
else {
|
|
pAppendBlock2 = pAppendParent;
|
|
pAppendParent = pAppendParent->GetParent();
|
|
}
|
|
}
|
|
|
|
pAppendBlock->Unlink();
|
|
|
|
if( pChild == 0 ){
|
|
m_pChild = pAppendChild;
|
|
}
|
|
else {
|
|
CEditElement *pOldNext = pChild->m_pNext;
|
|
pChild->SetNextSibling( pAppendChild );
|
|
|
|
// kind of sleazy. we haven't updated the link, GetLastChild will
|
|
// return the end of pAppendBlock's children.
|
|
GetLastChild()->SetNextSibling( pOldNext );
|
|
}
|
|
pAppendBlock->m_pChild = 0;
|
|
|
|
while( pAppendChild ){
|
|
pAppendChild->m_pParent = this;
|
|
pAppendChild = pAppendChild->GetNextSibling();
|
|
}
|
|
|
|
//
|
|
// LTNOTE: whack the container type to reflect the thing we just pulled
|
|
// up.
|
|
//
|
|
//XP_ASSERT( IsContainer() && pAppendBlock->IsContainer() );
|
|
if( bCopyAppendAttributes && IsContainer() && pAppendBlock->IsContainer() ){
|
|
Container()->CopyAttributes( pAppendBlock->Container() );
|
|
}
|
|
|
|
delete pAppendBlock;
|
|
}
|
|
|
|
// By default, yes for any non-atomic tag
|
|
XP_Bool CEditElement::IsContainerContainer(){
|
|
return !BitSet( edt_setNoEndTag, GetType());
|
|
}
|
|
|
|
//
|
|
// Search up the tree for the element that contains us.
|
|
// - start with us.
|
|
CEditContainerElement* CEditElement::FindContainer(){
|
|
CEditElement *pRet = this;
|
|
while( pRet ){
|
|
if ( pRet->IsSubDoc() && pRet != this ) break;
|
|
if( pRet->IsContainer() ){
|
|
return pRet->Container();
|
|
}
|
|
pRet = pRet->GetParent();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Search up the tree for the element that contains us.
|
|
// - skip us.
|
|
|
|
CEditContainerElement* CEditElement::FindEnclosingContainer(){
|
|
CEditElement *pRet = this->GetParent();
|
|
if ( pRet ){
|
|
return pRet->FindContainer();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void CEditElement::FindList( CEditContainerElement*& pContainer,
|
|
CEditListElement*& pList ) {
|
|
|
|
pList = 0;
|
|
XP_Bool bDone = FALSE;
|
|
pContainer = FindEnclosingContainer();
|
|
while( !bDone ){
|
|
if( pContainer->GetParent()->IsList() ){
|
|
pList = pContainer->GetParent()->List();
|
|
bDone = TRUE;
|
|
}
|
|
else {
|
|
CEditElement *pParentContainer = pContainer->FindEnclosingContainer();
|
|
if( pParentContainer ){
|
|
pContainer = pParentContainer->Container();
|
|
}
|
|
else {
|
|
bDone = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
CEditElement* CEditElement::FindContainerContainer(){
|
|
CEditElement *pRet = this;
|
|
while( pRet ){
|
|
// Don't need explicit subdoc test because
|
|
// sub-docs are paragraph containers.
|
|
if ( pRet->IsContainerContainer() ){
|
|
return pRet;
|
|
}
|
|
pRet = pRet->GetParent();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
void CEditElement::ValidateTree(){
|
|
CEditElement* pChild;
|
|
CEditElement* pLoopFinder;
|
|
// Make sure that all of our children point to us.
|
|
// Makes sure all children are valid.
|
|
// Make sure we don't have an infinite loop of children.
|
|
// Use a second pointer that goes
|
|
// around the loop twice as fast -- if it ever catches up with
|
|
// pChild, there's a loop. (And yes, since you're wondering,
|
|
// we did run into infinite loops here...)
|
|
pChild = GetChild();
|
|
pLoopFinder = pChild;
|
|
while( pChild ){
|
|
if( pChild->GetParent() != this ){
|
|
XP_ASSERT(FALSE);
|
|
}
|
|
XP_ASSERT(IsAcceptableChild(*pChild));
|
|
pChild->ValidateTree();
|
|
for ( int i = 0; i < 2; i++ ){
|
|
if ( pLoopFinder ) {
|
|
pLoopFinder = pLoopFinder->GetNextSibling();
|
|
if (pLoopFinder == pChild) {
|
|
XP_ASSERT(FALSE);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
pChild = pChild->GetNextSibling();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
CEditElement* CEditElement::Divide(int /* iOffset */){
|
|
return this;
|
|
}
|
|
|
|
XP_Bool CEditElement::DeleteElement(CEditElement* pTellIfKilled){
|
|
CEditElement *pKill = this;
|
|
CEditElement *pParent;
|
|
XP_Bool bKilled = FALSE;
|
|
CEditBuffer *pBuffer = GetEditBuffer();
|
|
if( pBuffer )
|
|
{
|
|
// Clear the table/cell selection if element is
|
|
// the selected cell or table, or is contained in a cell or table
|
|
// Also clears element saved in buffer if = to "this"
|
|
pBuffer->CleanupForDeletedElement(pKill);
|
|
}
|
|
do {
|
|
pParent = pKill->GetParent();
|
|
pKill->Unlink();
|
|
if( pKill == pTellIfKilled ){
|
|
bKilled = TRUE;
|
|
}
|
|
delete pKill;
|
|
pKill = pParent;
|
|
} while( pKill->GetChild() == 0 );
|
|
return bKilled;
|
|
}
|
|
|
|
void CEditElement::DeleteChildren(){
|
|
CEditElement* pNext = 0;
|
|
for ( CEditElement* pChild = GetChild();
|
|
pChild;
|
|
pChild = pNext ) {
|
|
pNext = pChild->GetNextSibling();
|
|
pChild->Unlink();
|
|
pChild->DeleteChildren();
|
|
delete pChild;
|
|
}
|
|
}
|
|
|
|
// class CEditSubDocElement
|
|
|
|
CEditSubDocElement::CEditSubDocElement(CEditElement *pParent, int tagType, char* pData)
|
|
: CEditElement(pParent, tagType, pData)
|
|
{
|
|
}
|
|
|
|
CEditSubDocElement::CEditSubDocElement(CEditElement *pParent, PA_Tag *pTag, int16 csid)
|
|
: CEditElement(pParent, pTag, csid)
|
|
{
|
|
}
|
|
|
|
CEditSubDocElement::CEditSubDocElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer)
|
|
: CEditElement(pStreamIn, pBuffer)
|
|
{
|
|
}
|
|
|
|
void CEditSubDocElement::FinishedLoad( CEditBuffer* pBuffer ){
|
|
CEditElement* pChild = GetChild();
|
|
if ( ! pChild ) {
|
|
// Subdocs have to have children.
|
|
// Put an empty paragraph into any empty subdoc.
|
|
pChild = CEditContainerElement::NewDefaultContainer( this,
|
|
IsCaption() ? ED_ALIGN_ABSCENTER : GetDefaultAlignment() );
|
|
// Creating it inserts it.
|
|
(void) new CEditTextElement(pChild, 0);
|
|
}
|
|
// Put any leaves into a container.
|
|
CEditContainerElement* pContainer = NULL;
|
|
CEditElement* pNext = 0;
|
|
for ( ;
|
|
pChild;
|
|
pChild = pNext) {
|
|
pNext = pChild->GetNextSibling(); // We might unlink pChild
|
|
if ( ! IsAcceptableChild(*pChild) ){
|
|
if ( pChild->IsLeaf() ){
|
|
if ( ! pContainer ){
|
|
pContainer = CEditContainerElement::NewDefaultContainer(NULL, GetDefaultAlignment());
|
|
pContainer->InsertAfter(pChild);
|
|
}
|
|
pChild->Unlink();
|
|
pChild->InsertAsLastChild(pContainer);
|
|
}
|
|
else {
|
|
XP_TRACE(("CEditSubDocElement: deleteing child of unknown type %d.\n",
|
|
pChild->GetElementType()));
|
|
delete pChild;
|
|
pChild = NULL;
|
|
}
|
|
}
|
|
else {
|
|
if ( pContainer ){
|
|
pContainer->FinishedLoad(pBuffer);
|
|
pContainer = NULL;
|
|
}
|
|
}
|
|
if ( pChild ) {
|
|
pChild->FinishedLoad(pBuffer);
|
|
}
|
|
}
|
|
if ( pContainer ){
|
|
pContainer->FinishedLoad(pBuffer);
|
|
pContainer = NULL;
|
|
}
|
|
}
|
|
|
|
XP_Bool CEditSubDocElement::Reduce( CEditBuffer* /* pBuffer */ ){
|
|
return FALSE;
|
|
}
|
|
|
|
#define LAYOUT_ARRAY_SIZE 500
|
|
|
|
// class CEditTableElement
|
|
|
|
// Default table params for constructors and ::NewData()
|
|
// Its alot easier to edit with > 1 pixels for these
|
|
// Easier to select, size, and drag cells, rows, and columns
|
|
#define ED_DEFAULT_TABLE_BORDER 3
|
|
#define ED_DEFAULT_CELL_SPACING 3
|
|
// Space between edge of cell and contents
|
|
// Extra here makes it easier to place caret inside a cell
|
|
#define ED_DEFAULT_CELL_PADDING 3
|
|
|
|
CEditTableElement::CEditTableElement(intn columns, intn rows)
|
|
: CEditElement(0, P_TABLE, 0),
|
|
m_backgroundColor(),
|
|
m_pBackgroundImage(0),
|
|
m_iBackgroundSaveIndex(0),
|
|
m_align(ED_ALIGN_LEFT),
|
|
m_malign(ED_ALIGN_DEFAULT),
|
|
m_iWidth(1),
|
|
m_iWidthPixels(1),
|
|
m_iHeight(1),
|
|
m_iHeightPixels(1),
|
|
m_bWidthPercent(FALSE),
|
|
m_bHeightPercent(FALSE),
|
|
m_iCellSpacing(ED_DEFAULT_CELL_SPACING),
|
|
m_iCellPadding(ED_DEFAULT_CELL_PADDING),
|
|
m_iCellBorder(0),
|
|
m_pFirstCellInColumnOrRow(0),
|
|
m_pNextCellInColumnOrRow(0),
|
|
m_iGetRow(0),
|
|
m_iRowY(0),
|
|
m_iColX(0),
|
|
m_bSpan(0),
|
|
m_iRows(0),
|
|
m_iColumns(0),
|
|
m_pCurrentCell(0),
|
|
m_bSaveWidthPercent(FALSE),
|
|
m_bSaveHeightPercent(FALSE),
|
|
m_bSaveWidthDefined(FALSE),
|
|
m_bSaveHeightDefined(FALSE),
|
|
m_iColumnLayoutSize(LAYOUT_ARRAY_SIZE),
|
|
m_iRowLayoutSize(LAYOUT_ARRAY_SIZE)
|
|
{
|
|
EDT_TRY {
|
|
for (intn row = 0; row < rows; row++ ){
|
|
CEditTableRowElement* pRow = new CEditTableRowElement(columns);
|
|
pRow->InsertAsFirstChild(this);
|
|
}
|
|
m_pColumnLayoutData = (EDT_CellLayoutData*)XP_ALLOC(LAYOUT_ARRAY_SIZE*sizeof(EDT_CellLayoutData));
|
|
m_pRowLayoutData = (EDT_CellLayoutData*)XP_ALLOC(LAYOUT_ARRAY_SIZE*sizeof(EDT_CellLayoutData));
|
|
}
|
|
EDT_CATCH_ALL {
|
|
Finalize();
|
|
EDT_RETHROW;
|
|
}
|
|
}
|
|
|
|
CEditTableElement::CEditTableElement(CEditElement *pParent, PA_Tag *pTag, int16 csid, ED_Alignment align)
|
|
: CEditElement(pParent, P_TABLE),
|
|
m_backgroundColor(),
|
|
m_pBackgroundImage(0),
|
|
m_iBackgroundSaveIndex(0),
|
|
m_align(align),
|
|
m_malign(ED_ALIGN_DEFAULT),
|
|
m_iWidth(1),
|
|
m_iWidthPixels(1),
|
|
m_iHeight(1),
|
|
m_iHeightPixels(1),
|
|
m_bWidthPercent(FALSE),
|
|
m_bHeightPercent(FALSE),
|
|
m_iCellSpacing(ED_DEFAULT_CELL_SPACING),
|
|
m_iCellPadding(ED_DEFAULT_CELL_PADDING),
|
|
m_iCellBorder(0),
|
|
m_pFirstCellInColumnOrRow(0),
|
|
m_pNextCellInColumnOrRow(0),
|
|
m_iGetRow(0),
|
|
m_iRowY(0),
|
|
m_iColX(0),
|
|
m_bSpan(0),
|
|
m_iRows(0),
|
|
m_iColumns(0),
|
|
m_pCurrentCell(0),
|
|
m_bSaveWidthPercent(FALSE),
|
|
m_bSaveHeightPercent(FALSE),
|
|
m_bSaveWidthDefined(FALSE),
|
|
m_bSaveHeightDefined(FALSE),
|
|
m_iColumnLayoutSize(LAYOUT_ARRAY_SIZE),
|
|
m_iRowLayoutSize(LAYOUT_ARRAY_SIZE)
|
|
{
|
|
switch( m_align ) {
|
|
case ED_ALIGN_CENTER:
|
|
/* OK case -- 'center' tag. */
|
|
m_align = ED_ALIGN_ABSCENTER;
|
|
break;
|
|
case ED_ALIGN_LEFT:
|
|
case ED_ALIGN_ABSCENTER:
|
|
case ED_ALIGN_RIGHT:
|
|
break;
|
|
default:
|
|
XP_ASSERT(FALSE);
|
|
/* Falls through */
|
|
case ED_ALIGN_DEFAULT:
|
|
m_align = ED_ALIGN_LEFT;
|
|
break;
|
|
}
|
|
if( pTag ){
|
|
EDT_TableData *pData = ParseParams(pTag, csid);
|
|
pData->align = m_align;
|
|
SetData(pData);
|
|
FreeData(pData);
|
|
}
|
|
m_pColumnLayoutData = (EDT_CellLayoutData*)XP_ALLOC(LAYOUT_ARRAY_SIZE*sizeof(EDT_CellLayoutData));
|
|
m_pRowLayoutData = (EDT_CellLayoutData*)XP_ALLOC(LAYOUT_ARRAY_SIZE*sizeof(EDT_CellLayoutData));
|
|
}
|
|
|
|
CEditTableElement::CEditTableElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer)
|
|
: CEditElement(pStreamIn, pBuffer),
|
|
m_backgroundColor(),
|
|
m_iBackgroundSaveIndex(0),
|
|
m_pBackgroundImage(0),
|
|
m_iWidth(1),
|
|
m_iWidthPixels(1),
|
|
m_iHeight(1),
|
|
m_iHeightPixels(1),
|
|
m_bWidthPercent(FALSE),
|
|
m_bHeightPercent(FALSE),
|
|
m_iCellSpacing(ED_DEFAULT_CELL_SPACING),
|
|
m_iCellPadding(ED_DEFAULT_CELL_PADDING),
|
|
m_iCellBorder(0),
|
|
m_pFirstCellInColumnOrRow(0),
|
|
m_pNextCellInColumnOrRow(0),
|
|
m_iGetRow(0),
|
|
m_iRowY(0),
|
|
m_iColX(0),
|
|
m_bSpan(0),
|
|
m_iRows(0),
|
|
m_iColumns(0),
|
|
m_pCurrentCell(0),
|
|
m_bSaveWidthPercent(FALSE),
|
|
m_bSaveHeightPercent(FALSE),
|
|
m_bSaveWidthDefined(FALSE),
|
|
m_bSaveHeightDefined(FALSE),
|
|
m_iColumnLayoutSize(LAYOUT_ARRAY_SIZE),
|
|
m_iRowLayoutSize(LAYOUT_ARRAY_SIZE)
|
|
{
|
|
m_align = (ED_Alignment) pStreamIn->ReadInt();
|
|
m_malign = (ED_Alignment) pStreamIn->ReadInt();
|
|
|
|
// Get size data from stream -- needed for pasting tables/cells
|
|
// We must get CSID from buffer since element is not in doc yet
|
|
//TODO: Should we pass in CSID from paste stream instead?
|
|
PA_Tag* pTag = CEditElement::TagOpen(0);
|
|
EDT_TableData *pData = ParseParams( pTag, pBuffer->GetRAMCharSetID() );
|
|
if(pData)
|
|
{
|
|
m_iWidth = pData->iWidth;
|
|
m_bWidthPercent = pData->bWidthPercent;
|
|
if( !m_bWidthPercent )
|
|
m_iWidthPixels = m_iWidth;
|
|
|
|
m_iHeight = pData->iHeight;
|
|
m_bHeightPercent = pData->bHeightPercent;
|
|
if( !m_bHeightPercent )
|
|
m_iHeightPixels = m_iHeight;
|
|
|
|
m_iCellSpacing = pData->iCellSpacing;
|
|
m_iCellPadding = pData->iCellPadding;
|
|
|
|
if( pData->bBorderWidthDefined && pData->iBorderWidth > 0 )
|
|
m_iCellBorder = 1;
|
|
else
|
|
m_iCellBorder = 0;
|
|
|
|
EDT_FreeTableData(pData);
|
|
}
|
|
PA_FreeTag( pTag );
|
|
|
|
m_pColumnLayoutData = (EDT_CellLayoutData*)XP_ALLOC(LAYOUT_ARRAY_SIZE*sizeof(EDT_CellLayoutData));
|
|
m_pRowLayoutData = (EDT_CellLayoutData*)XP_ALLOC(LAYOUT_ARRAY_SIZE*sizeof(EDT_CellLayoutData));
|
|
}
|
|
|
|
CEditTableElement::~CEditTableElement(){
|
|
delete m_pBackgroundImage;
|
|
DeleteLayoutData();
|
|
|
|
// Be sure to unselect the table if it is selected
|
|
CEditBuffer *pBuffer = GetEditBuffer();
|
|
if( pBuffer && (pBuffer->GetSelectedTable() == this) )
|
|
{
|
|
pBuffer->SelectTable(FALSE, NULL, this);
|
|
}
|
|
}
|
|
|
|
PA_Tag* CEditTableElement::TagOpen( int iEditOffset ){
|
|
return InternalTagOpen(iEditOffset, FALSE);
|
|
}
|
|
|
|
PA_Tag* CEditTableElement::InternalTagOpen( int iEditOffset, XP_Bool bForPrinting){
|
|
PA_Tag *pRet = 0;
|
|
PA_Tag* pTag;
|
|
|
|
// create the DIV tag if we need to.
|
|
if( m_align == ED_ALIGN_ABSCENTER || m_align == ED_ALIGN_RIGHT ){
|
|
pTag = XP_NEW( PA_Tag );
|
|
XP_BZERO( pTag, sizeof( PA_Tag ) );
|
|
if( m_align== ED_ALIGN_RIGHT ){
|
|
SetTagData( pTag, "ALIGN=right>");
|
|
pTag->type = P_DIVISION;
|
|
}
|
|
else {
|
|
SetTagData( pTag, ">");
|
|
pTag->type = P_CENTER;
|
|
}
|
|
pRet = pTag;
|
|
}
|
|
// create the actual table tag
|
|
|
|
EDT_TableData* pTableData = GetData();
|
|
char* pTagData = CreateTagData(pTableData, bForPrinting);
|
|
if ( pTagData ) {
|
|
pTag = XP_NEW( PA_Tag );
|
|
XP_BZERO( pTag, sizeof( PA_Tag ) );
|
|
SetTagData( pTag, pTagData );
|
|
XP_FREE(pTagData);
|
|
}
|
|
else {
|
|
pTag = CEditElement::TagOpen( iEditOffset );
|
|
}
|
|
FreeData(pTableData);
|
|
|
|
// link the tags together.
|
|
if( pRet == 0 ){
|
|
pRet = pTag;
|
|
}
|
|
else {
|
|
pRet->next = pTag;
|
|
}
|
|
return pRet;
|
|
}
|
|
|
|
PA_Tag* CEditTableElement::TagEnd( ){
|
|
PA_Tag *pRet = CEditElement::TagEnd();
|
|
if( m_align == ED_ALIGN_ABSCENTER || m_align == ED_ALIGN_RIGHT ){
|
|
PA_Tag* pTag = XP_NEW( PA_Tag );
|
|
XP_BZERO( pTag, sizeof( PA_Tag ) );
|
|
pTag->is_end = TRUE;
|
|
if( m_align == ED_ALIGN_RIGHT ){
|
|
pTag->type = P_DIVISION;
|
|
}
|
|
else {
|
|
pTag->type = P_CENTER;
|
|
}
|
|
|
|
if( pRet == 0 ){
|
|
pRet = pTag;
|
|
}
|
|
else {
|
|
pRet->next = pTag;
|
|
}
|
|
}
|
|
return pRet;
|
|
}
|
|
|
|
CEditTableRowElement* CEditTableElement::GetFirstRow()
|
|
{
|
|
CEditElement *pChild = GetChild();
|
|
if( pChild && pChild->IsTableRow() )
|
|
{
|
|
return pChild->TableRow();
|
|
}
|
|
else if( pChild )
|
|
{
|
|
// Most likely we have a caption
|
|
XP_ASSERT(pChild->IsCaption());
|
|
pChild = pChild->GetNextSibling();
|
|
if( pChild && pChild->IsTableRow() )
|
|
return pChild->TableRow();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
CEditTableCellElement* CEditTableElement::GetFirstCell()
|
|
{
|
|
CEditElement *pChild = GetChild();
|
|
if( pChild )
|
|
{
|
|
// Skip over caption
|
|
if( pChild->IsCaption() )
|
|
pChild = pChild->GetNextSibling();
|
|
|
|
if( pChild && pChild->IsTableRow() )
|
|
{
|
|
pChild = pChild->GetChild();
|
|
if( pChild && pChild->IsTableCell() )
|
|
{
|
|
// Initialize these also so we can follow
|
|
// this call with GetNextCellInRow(), GetNextCellInColumn(),
|
|
// or GetNextCellInTable()
|
|
m_pCurrentCell = pChild->TableCell();
|
|
m_pFirstCellInColumnOrRow = m_pNextCellInColumnOrRow = m_pCurrentCell;
|
|
return m_pCurrentCell;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
CEditTableCellElement* CEditTableElement::GetNextCellInTable(intn *pRowCounter)
|
|
{
|
|
if( m_pCurrentCell )
|
|
m_pCurrentCell = m_pCurrentCell->GetNextCellInTable(pRowCounter);
|
|
return m_pCurrentCell;
|
|
}
|
|
|
|
// The number of rows can simply be counted
|
|
intn CEditTableElement::CountRows()
|
|
{
|
|
m_iRows = 0;
|
|
for ( CEditElement* pChild = GetChild();
|
|
pChild;
|
|
pChild = pChild->GetNextSibling())
|
|
{
|
|
if ( pChild->IsTableRow() )
|
|
{
|
|
m_iRows++;
|
|
}
|
|
}
|
|
return m_iRows;
|
|
}
|
|
|
|
// Counting columns is more difficult because ROWSPAN > 1 in a cell
|
|
// pushes cells in following rows to the right.
|
|
// Thus the total number of geometric columns may be greater
|
|
// than a simple count of the number of cells in a row
|
|
// So we use a strategy similar to how we fill in m_pColumnLayoutData:
|
|
// we count the number of unique X values for all cells
|
|
intn CEditTableElement::CountColumns()
|
|
{
|
|
TXP_GrowableArray_int32 X_Array;
|
|
CEditTableCellElement *pCell = GetFirstCell();
|
|
|
|
while( pCell )
|
|
{
|
|
int32 iCellX = pCell->GetX();
|
|
XP_Bool bFound = FALSE;
|
|
|
|
// See if cell's X was already found
|
|
intn iSize = X_Array.Size();
|
|
for( intn i=0; i < iSize; i++ )
|
|
{
|
|
if( iCellX == X_Array[i] )
|
|
{
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if( !bFound )
|
|
X_Array.Add(iCellX);
|
|
|
|
pCell = GetNextCellInTable();
|
|
}
|
|
m_iColumns = X_Array.Size();
|
|
|
|
return m_iColumns;
|
|
}
|
|
|
|
CEditTableRowElement* CEditTableElement::GetRow(int32 Y, intn *pRow)
|
|
{
|
|
intn iRow = GetRowIndex(Y);
|
|
if( iRow >= 0 )
|
|
{
|
|
CEditTableCellElement *pCell = GetFirstCellAtRowIndex(iRow);
|
|
if( pRow )
|
|
*pRow = iRow;
|
|
|
|
if( pCell )
|
|
return (CEditTableRowElement*)pCell->GetParent();
|
|
}
|
|
return NULL;
|
|
|
|
// Old method -- less efficient.
|
|
// Needed only if we must use GetRow() before table is layed out,
|
|
// because thats when the m_pRowLayoutData is built (in CEditBuffer::FixupTableData())
|
|
#if 0
|
|
for ( CEditElement* pChild = GetChild();
|
|
pChild;
|
|
pChild = pChild->GetNextSibling())
|
|
{
|
|
if ( pChild->IsTableRow() )
|
|
{
|
|
// Scan row for first cell that has given Y as top
|
|
CEditTableCellElement *pCell = (CEditTableCellElement*)(pChild->GetChild());
|
|
while( pCell && pCell-IsTableCell() )
|
|
{
|
|
if( Y == pCell->GetY() )
|
|
{
|
|
if( pRow )
|
|
*pRow = iRow;
|
|
return pChild->TableRow();
|
|
}
|
|
pCell = (CEditTableCellElement*)(pCell->GetNextSibling());
|
|
}
|
|
iRow++;
|
|
}
|
|
}
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
CEditTableCellElement* CEditTableElement::GetFirstCellInRow(CEditTableCellElement *pCell, XP_Bool bSpan)
|
|
{
|
|
return GetFirstCellInRow(pCell->GetY(), bSpan);
|
|
}
|
|
|
|
// Get first cell of a "real" (geometric) row, based on location
|
|
// If bSpan == TRUE , this will also get a cell that spans given Y,
|
|
// even if top of cell != Y
|
|
// Note: When bSpan is TRUE, we will find a cell in row above if it spans the
|
|
// given Y. This is OK for current uses, but it really doesn't return
|
|
// the "first" cell. (See comment below)
|
|
// If we don't return the spanned cell as "first", GetNextCellInRow would have
|
|
// to be much more complicated, as it would have to scan rows above to
|
|
// detect cells spanning the current row
|
|
CEditTableCellElement* CEditTableElement::GetFirstCellInRow(int32 Y, XP_Bool bSpan)
|
|
{
|
|
|
|
CEditTableCellElement *pCell = GetFirstCell();
|
|
m_iGetRow = 0;
|
|
m_iRowY = Y;
|
|
while( pCell )
|
|
{
|
|
int32 iCellY = pCell->GetY();
|
|
// Note that a cell can really be FIRST in a spanned row only if it is
|
|
// really the first cell in logical row (check for previous sibling)
|
|
// but if we do that, we won't hit the cell at all later.
|
|
if( (bSpan && Y >= iCellY && Y <= (iCellY + pCell->GetHeight()) /*&&
|
|
pCell->GetPreviousSibling() == NULL*/ ) ||
|
|
(!bSpan && ( Y == iCellY)) )
|
|
{
|
|
m_pFirstCellInColumnOrRow = m_pNextCellInColumnOrRow = pCell;
|
|
return pCell;
|
|
}
|
|
pCell = pCell->GetNextCellInTable(&m_iGetRow);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// Get next cell at or spaning the same geometric row as found by GetFirstCellInRow()
|
|
// or use Y from supplied cell
|
|
CEditTableCellElement* CEditTableElement::GetNextCellInRow(CEditTableCellElement *pCell)
|
|
{
|
|
if( pCell )
|
|
{
|
|
// Initialize using given cell,
|
|
// so we don't always have to call GetFirst...
|
|
m_pNextCellInColumnOrRow = pCell;
|
|
m_iRowY = pCell->GetY();
|
|
m_bSpan = FALSE;
|
|
}
|
|
pCell = m_pNextCellInColumnOrRow->GetNextCellInTable(&m_iGetRow);
|
|
|
|
while( pCell )
|
|
{
|
|
int32 iCellY = pCell->GetY();
|
|
if( (m_bSpan && (m_iRowY >= iCellY) && (m_iRowY <= (iCellY + pCell->GetHeight()))) ||
|
|
(!m_bSpan && ( m_iRowY == iCellY)) )
|
|
{
|
|
m_pNextCellInColumnOrRow = pCell;
|
|
return pCell;
|
|
}
|
|
pCell = pCell->GetNextCellInTable(&m_iGetRow);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
CEditTableCellElement* CEditTableElement::GetLastCellInRow(CEditTableCellElement *pCell)
|
|
{
|
|
if( pCell )
|
|
{
|
|
// Move until next cell doesn't exist
|
|
CEditTableCellElement *pNextCell;
|
|
while( pCell && (pNextCell = GetNextCellInRow(pCell)) != NULL )
|
|
{
|
|
pCell = pNextCell;
|
|
}
|
|
}
|
|
return pCell;
|
|
}
|
|
|
|
CEditTableCellElement* CEditTableElement::GetPreviousCellInRow(CEditTableCellElement *pCell)
|
|
{
|
|
if( pCell )
|
|
{
|
|
// Start with the first cell..
|
|
CEditTableCellElement *pFirstCell = GetFirstCellInRow(pCell->GetY());
|
|
// We are the first cell - no previous exitst
|
|
if( pFirstCell == pCell )
|
|
return NULL;
|
|
|
|
// Move until we find cell before us
|
|
CEditTableCellElement *pPrevCell = pFirstCell;
|
|
CEditTableCellElement *pNextCell;
|
|
while( pPrevCell && (pNextCell = GetNextCellInRow(pPrevCell)) != pCell )
|
|
{
|
|
pPrevCell = pNextCell;
|
|
}
|
|
return pPrevCell;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
CEditTableCellElement* CEditTableElement::GetFirstCellAtColumnIndex(intn iIndex)
|
|
{
|
|
if( iIndex >= 0 && iIndex < m_iColumns )
|
|
return m_pColumnLayoutData[iIndex].pEdCell;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
CEditTableCellElement* CEditTableElement::GetFirstCellAtRowIndex(intn iIndex)
|
|
{
|
|
if( iIndex >= 0 && iIndex < m_iRows )
|
|
return m_pRowLayoutData[iIndex].pEdCell;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// Get the defining left (for columns) and top (for rows) value from index
|
|
int32 CEditTableElement::GetColumnX(intn iIndex)
|
|
{
|
|
if( iIndex < m_iColumns )
|
|
return m_pColumnLayoutData[iIndex].X;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32 CEditTableElement::GetRowY(intn iIndex)
|
|
{
|
|
if( iIndex < m_iRows )
|
|
return m_pRowLayoutData[iIndex].Y;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Get grid coordinates of a cell
|
|
intn CEditTableElement::GetColumnIndex(int32 X)
|
|
{
|
|
for( intn i=0; i < m_iColumns; i++ )
|
|
{
|
|
if( m_pColumnLayoutData[i].X == X )
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
intn CEditTableElement::GetRowIndex(int32 Y)
|
|
{
|
|
for( intn i=0; i < m_iRows; i++ )
|
|
{
|
|
if( m_pRowLayoutData[i].Y == Y )
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
CEditTableCellElement* CEditTableElement::GetFirstCellInColumn(CEditTableCellElement *pCell, XP_Bool bSpan)
|
|
{
|
|
return GetFirstCellInColumn(pCell->GetX(), bSpan);
|
|
}
|
|
|
|
// Get first cell of a "real" (geometric) row, based on location
|
|
// If bSpan is TRUE, this will also get a cell that spans
|
|
// given X, even if left edge of cell < X
|
|
CEditTableCellElement* CEditTableElement::GetFirstCellInColumn(int32 X, XP_Bool bSpan)
|
|
{
|
|
CEditTableCellElement *pCell = GetFirstCell();
|
|
m_iGetRow = 0;
|
|
m_iColX = X;
|
|
m_bSpan = bSpan;
|
|
while( pCell )
|
|
{
|
|
int32 iCellX = pCell->GetX();
|
|
// Note: Using GetWidth() is OK (and more efficient) here since all that matters is
|
|
// the cell's right edge spans (is > than) left-edge X value
|
|
if( (!bSpan && X == iCellX) ||
|
|
( bSpan && X >= iCellX && X <= (iCellX + pCell->GetWidth()) ) )
|
|
{
|
|
m_pFirstCellInColumnOrRow = m_pNextCellInColumnOrRow = pCell;
|
|
return pCell;
|
|
}
|
|
pCell = pCell->GetNextCellInTable(&m_iGetRow);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// Get next cell at or spaning the same geometric row as found by GetFirstCellInColumn()
|
|
// or use X from supplied cell
|
|
CEditTableCellElement* CEditTableElement::GetNextCellInColumn(CEditTableCellElement *pCell)
|
|
{
|
|
if( pCell )
|
|
{
|
|
// Initialize using given cell,
|
|
// so we don't always have to call GetFirst...
|
|
m_pNextCellInColumnOrRow = pCell;
|
|
m_iColX = pCell->GetX();
|
|
m_bSpan = FALSE;
|
|
}
|
|
pCell = m_pNextCellInColumnOrRow->GetNextCellInTable(&m_iGetRow);
|
|
|
|
while( pCell )
|
|
{
|
|
int32 iCellX = pCell->GetX();
|
|
if( (!m_bSpan && m_iColX == iCellX) || // Must have exact match
|
|
( m_bSpan && m_iColX >= iCellX && m_iColX <= (iCellX + pCell->GetWidth()) ) ) //GetWidth is OK here
|
|
{
|
|
m_pNextCellInColumnOrRow = pCell;
|
|
return pCell;
|
|
}
|
|
pCell = pCell->GetNextCellInTable(&m_iGetRow);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
CEditTableCellElement* CEditTableElement::GetLastCellInColumn(CEditTableCellElement *pCell)
|
|
{
|
|
if( pCell )
|
|
{
|
|
// Move until next cell doesn't exist
|
|
CEditTableCellElement *pNextCell;
|
|
while( pCell && (pNextCell = GetNextCellInColumn(pCell)) != NULL )
|
|
{
|
|
pCell = pNextCell;
|
|
}
|
|
}
|
|
return pCell;
|
|
}
|
|
|
|
CEditTableCellElement* CEditTableElement::GetPreviousCellInColumn(CEditTableCellElement *pCell)
|
|
{
|
|
if( pCell )
|
|
{
|
|
// Start with the first cell..
|
|
CEditTableCellElement *pFirstCell = GetFirstCellInColumn(pCell->GetX());
|
|
// We are the first cell - no previous exitst
|
|
if( pFirstCell == pCell )
|
|
return NULL;
|
|
|
|
// Move until we find cell before us
|
|
CEditTableCellElement *pPrevCell = pFirstCell;
|
|
CEditTableCellElement *pNextCell;
|
|
while( pPrevCell && (pNextCell = GetNextCellInColumn(pPrevCell)) != pCell )
|
|
{
|
|
pPrevCell = pNextCell;
|
|
}
|
|
return pPrevCell;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
CEditTableCellElement* CEditTableElement::GetFirstCellInNextColumn(int32 iCurrentColumnX)
|
|
{
|
|
intn i;
|
|
|
|
// Scan data to find current cell's column in our layout data
|
|
for( i = 0; i < m_iColumns; i++ )
|
|
{
|
|
if( iCurrentColumnX == m_pColumnLayoutData[i].X )
|
|
break;
|
|
}
|
|
if( i+1 >= m_iColumns )
|
|
return NULL; // No cell is after the current column
|
|
|
|
return m_pColumnLayoutData[i+1].pEdCell;
|
|
}
|
|
|
|
CEditTableCellElement* CEditTableElement::GetFirstCellInNextRow(int32 iCurrentRowY)
|
|
{
|
|
intn i;
|
|
|
|
// Scan data to find current cell's row in our layout data
|
|
for( i = 0; i < m_iRows; i++ )
|
|
{
|
|
if( iCurrentRowY == m_pRowLayoutData[i].Y )
|
|
break;
|
|
}
|
|
if( i+1 >= m_iRows )
|
|
return NULL; // No cell is after the current row
|
|
|
|
return m_pRowLayoutData[i+1].pEdCell;
|
|
}
|
|
|
|
// Figure out how many columns are included between given X values
|
|
intn CEditTableElement::GetColumnsSpanned(int32 iStartX, int32 iEndX)
|
|
{
|
|
if( iStartX == iEndX )
|
|
return 0;
|
|
|
|
intn iColumns = 0;
|
|
for( intn i = 0; i < m_iColumns; i++ )
|
|
{
|
|
int32 iColX = m_pColumnLayoutData[i].X;
|
|
if( iStartX <= iColX && iEndX > iColX )
|
|
iColumns++;
|
|
}
|
|
return iColumns;
|
|
}
|
|
|
|
// Test if any cell in the first row has COLSPAN > 1
|
|
XP_Bool CEditTableElement::FirstRowHasColSpan()
|
|
{
|
|
CEditTableCellElement* pCell = GetFirstCell();
|
|
while( pCell )
|
|
{
|
|
if( pCell->GetColSpan() > 1 )
|
|
return TRUE;
|
|
|
|
pCell = GetNextCellInRow();
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Currently (4/24/98) not called
|
|
// This should be done at least the before the first time a table is layed out
|
|
// when a file is loaded. It fixes problems that cause major headaches with
|
|
// rowspan and columnspan
|
|
// TODO: Should we also pad rows to insure that table is rectangular? (no ragged right edge)
|
|
void CEditTableElement::FixupColumnsAndRows()
|
|
{
|
|
CEditTableCellElement *pFirstCell = GetFirstCell();
|
|
if( !pFirstCell )
|
|
return;
|
|
|
|
TXP_GrowableArray_int32 X_Array;
|
|
TXP_GrowableArray_int32 Y_Array;
|
|
CEditTableCellElement *pCell = pFirstCell;
|
|
|
|
while( pCell )
|
|
{
|
|
int32 iCellX = pCell->GetX();
|
|
int32 iCellY = pCell->GetY();
|
|
XP_Bool bFoundX = FALSE;
|
|
|
|
// See if cell's X was already found
|
|
intn iSize = X_Array.Size();
|
|
for( intn i=0; i < iSize; i++ )
|
|
{
|
|
if( iCellX == X_Array[i] )
|
|
{
|
|
bFoundX = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if( !bFoundX )
|
|
X_Array.Add(iCellX);
|
|
|
|
pCell = GetNextCellInTable();
|
|
}
|
|
m_iColumns = X_Array.Size();
|
|
|
|
intn iColumns = CountColumns();
|
|
intn iRows = CountRows();
|
|
intn i, iMinSpan, iDecrease;
|
|
// We will find the maximum number of columns in all rows
|
|
m_iColumns = 0;
|
|
|
|
for ( i = 0; i < iColumns; i++ )
|
|
{
|
|
CEditTableCellElement *pFirstCellInCol = m_pColumnLayoutData[i].pEdCell;
|
|
pCell = pFirstCellInCol;
|
|
iMinSpan = 2;
|
|
|
|
// Scan column to check for minimum COLSPAN
|
|
while( pCell )
|
|
{
|
|
if( pCell->GetColSpan() < iMinSpan )
|
|
iMinSpan = pCell->GetColSpan();
|
|
|
|
// Any cell with value of 1 means column is OK
|
|
if( iMinSpan == 1 )
|
|
break;
|
|
|
|
pCell = GetNextCellInColumn(pCell);
|
|
}
|
|
if( iMinSpan > 1 )
|
|
{
|
|
// We have a bad column
|
|
// Go back through to fixup COLSPAN values
|
|
pCell = pFirstCellInCol;
|
|
iDecrease = iMinSpan - 1;
|
|
|
|
while( pCell )
|
|
{
|
|
pCell->DecreaseColSpan(iDecrease);
|
|
pCell = GetNextCellInColumn(pCell);
|
|
}
|
|
}
|
|
intn iCellsInRow = m_pColumnLayoutData[i].iCellsInRow;
|
|
CEditTableRowElement *pRow = (CEditTableRowElement*)pCell->GetParent();
|
|
if( pRow )
|
|
pRow->SetColumns(iCellsInRow);
|
|
|
|
if( m_iColumns > iCellsInRow)
|
|
m_iColumns = iCellsInRow;
|
|
}
|
|
|
|
// Do the same for ROWSPAN
|
|
for ( i = 0; i < iRows; i++ )
|
|
{
|
|
CEditTableCellElement *pFirstCellInRow = m_pRowLayoutData[i].pEdCell;
|
|
pCell = pFirstCellInRow;
|
|
iMinSpan = 2;
|
|
|
|
// Scan Row to check for minimum ROWSPAN
|
|
while( pCell )
|
|
{
|
|
if( pCell->GetRowSpan() < iMinSpan )
|
|
iMinSpan = pCell->GetRowSpan();
|
|
|
|
if( iMinSpan == 1 ) // Row is OK
|
|
break;
|
|
|
|
pCell = GetNextCellInRow(pCell);
|
|
}
|
|
if( iMinSpan > 1 )
|
|
{
|
|
// We have a bad row
|
|
// Go back through to fixup ROWSPAN values
|
|
pCell = pFirstCellInRow;
|
|
iDecrease = iMinSpan - 1;
|
|
|
|
while( pCell )
|
|
{
|
|
pCell->DecreaseRowSpan(iDecrease);
|
|
pCell = GetNextCellInRow(pCell);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CEditTableElement::NormalizeCellsPerRow()
|
|
{
|
|
CEditTableCellElement *pFirstCell = GetFirstCell();
|
|
CEditBuffer *pBuffer = GetEditBuffer();
|
|
if( !pFirstCell || !pBuffer )
|
|
return;
|
|
|
|
int32 iRows = CountRows();
|
|
int32 iArraySize = iRows * sizeof(int32);
|
|
int32 *pCellsPerRow = (int32*)XP_ALLOC(iArraySize);
|
|
if( !pCellsPerRow )
|
|
return;
|
|
|
|
XP_MEMSET( pCellsPerRow, 0, iArraySize );
|
|
|
|
CEditTableCellElement *pCell = pFirstCell;
|
|
intn iRow = 0;
|
|
intn iPrevRow = 0;
|
|
int32 iCellsInRow = 0;
|
|
|
|
while( pCell )
|
|
{
|
|
intn iColSpan = pCell->GetColSpan();
|
|
intn iRowSpan = pCell->GetRowSpan();
|
|
|
|
pCellsPerRow[iRow] += iColSpan;
|
|
|
|
// Fixup subsequent rows because of rowspan
|
|
if( iRowSpan > 1 )
|
|
{
|
|
for( intn j = 1; j < iRowSpan; j++ )
|
|
{
|
|
// We may overrun our array if table is "bad"
|
|
// because of a ROWSPAN value that exceeds actual
|
|
// number of rows. Just skip attempts to access a value too high
|
|
// since there is no row to use the "ExtraColumns" anyway.
|
|
if( iRow+j < iRows )
|
|
pCellsPerRow[iRow+j] += iColSpan;
|
|
else
|
|
// Fixup bad cell????
|
|
;//pCell->SetRowSpan(iRows-1);
|
|
}
|
|
}
|
|
pCell = pCell->GetNextCellInTable(&iRow);
|
|
}
|
|
|
|
XP_ASSERT((iRow+1) == iRows); // Safety check
|
|
|
|
// Find the maximum cells per row
|
|
intn iTotalRows = pCellsPerRow[0];
|
|
intn i;
|
|
|
|
for( i = 1; i < iRows; i++ )
|
|
{
|
|
if( pCellsPerRow[i] > iTotalRows )
|
|
iTotalRows = pCellsPerRow[i];
|
|
}
|
|
|
|
pBuffer->SetFillNewCellWithSpace();
|
|
CEditTableRowElement *pRow = GetFirstRow();
|
|
|
|
// Now go through rows and add cells as needed
|
|
for( i = 0; i < iRows; i++ )
|
|
{
|
|
XP_ASSERT(pRow);
|
|
if( !pRow )
|
|
break;
|
|
|
|
intn iExtraCells = iTotalRows - pCellsPerRow[i];
|
|
for( intn j = 0; j < iExtraCells; j++ )
|
|
{
|
|
CEditTableCellElement *pNewCell = new CEditTableCellElement();
|
|
if( pNewCell )
|
|
{
|
|
// What could we set this to to make CountColumns() work?
|
|
//pNewCell->SetX(????);
|
|
pNewCell->InsertAsLastChild(pRow);
|
|
pNewCell->FinishedLoad(pBuffer);
|
|
}
|
|
}
|
|
pRow = pRow->GetNextRow();
|
|
}
|
|
pBuffer->ClearFillNewCellWithSpace();
|
|
}
|
|
|
|
void CEditTableElement::InsertRows(int32 Y, int32 iNewY, intn number,
|
|
CEditTableElement* pSourceTable, intn iStartColumn,
|
|
CEditTableCellElement **ppCellForInsertPoint)
|
|
{
|
|
if ( number == 0 )
|
|
{
|
|
return; /* A waste of time, but legal. */
|
|
}
|
|
if ( number < 0 || Y < 0 )
|
|
{
|
|
/* Illegal. */
|
|
XP_ASSERT(FALSE);
|
|
return;
|
|
}
|
|
CEditTableRowElement* pCurrentRow = GetRow(Y);
|
|
if ( ! pCurrentRow ) {
|
|
XP_ASSERT(FALSE);
|
|
return;
|
|
}
|
|
// We must examine cells with same geometric row,
|
|
// but in different logical rows
|
|
// So scan all cells near given Y and increase ROWSPAN
|
|
// where needed and count actual number of cells
|
|
// to be inserted in new row(s)
|
|
intn iColumns = 0;
|
|
|
|
CEditBuffer *pBuffer = GetEditBuffer();
|
|
XP_ASSERT(pBuffer);
|
|
CEditTableCellElement *pCellForInsertPoint = NULL;
|
|
|
|
if( pSourceTable )
|
|
{
|
|
CEditTableRowElement *pSourceRow;
|
|
|
|
if( iStartColumn > 0 )
|
|
{
|
|
// Set flag to autoinsert space into
|
|
// the blank new cells we will insert here
|
|
pBuffer->SetFillNewCellWithSpace();
|
|
|
|
//Simplest way to handle offset insert column
|
|
// is to add blank cells to the source data
|
|
pSourceRow = pSourceTable->GetFirstRow();
|
|
while(pSourceRow)
|
|
{
|
|
for( intn i = 0; i < iStartColumn; i++ )
|
|
{
|
|
CEditTableCellElement *pNewCell = new CEditTableCellElement();
|
|
if( pNewCell )
|
|
{
|
|
// Fake the X value so CountColumns will work
|
|
pNewCell->SetX(iStartColumn - i);
|
|
pNewCell->InsertAsFirstChild(pSourceRow);
|
|
pNewCell->FinishedLoad(pBuffer);
|
|
}
|
|
}
|
|
pSourceRow = pSourceRow->GetNextRow();
|
|
}
|
|
// Clear this so we don't touch empty cells
|
|
// from the paste source
|
|
pBuffer->ClearFillNewCellWithSpace();
|
|
}
|
|
}
|
|
|
|
CEditTableCellElement *pCell = GetFirstCellInRow(Y, TRUE );
|
|
while( pCell )
|
|
{
|
|
int32 iCellY = pCell->GetY();
|
|
int32 iColSpan = pCell->GetColSpan();
|
|
int32 iRowSpan = pCell->GetRowSpan();
|
|
if( Y == iNewY )
|
|
{
|
|
// Inserting above
|
|
|
|
if( iCellY == Y )
|
|
{
|
|
// We will be inserting cells above this one
|
|
iColumns += iColSpan;
|
|
} else {
|
|
// Cell is actually in row above,
|
|
// so just increase its rowspan
|
|
pCell->IncreaseRowSpan(number);
|
|
}
|
|
} else {
|
|
// Inserting below current row
|
|
if( iNewY == (iCellY + pCell->GetHeight()) )
|
|
{
|
|
// Bottom of this cell is where we want to insert
|
|
iColumns += iColSpan;
|
|
} else {
|
|
// Cell spans the new insert Y, so just expand some more
|
|
pCell->IncreaseRowSpan(number);
|
|
}
|
|
}
|
|
pCell = GetNextCellInRow();
|
|
}
|
|
|
|
// Now insert the new rows (including iColumns new cells in each)
|
|
for ( intn row = 0; row < number; row++ )
|
|
{
|
|
CEditTableRowElement* pNewRow;
|
|
if ( pSourceTable )
|
|
{
|
|
pNewRow = pSourceTable->GetFirstRow();
|
|
pNewRow->Unlink();
|
|
}
|
|
else
|
|
{
|
|
pNewRow = new CEditTableRowElement(iColumns);
|
|
if( !pNewRow )
|
|
break;
|
|
}
|
|
// Set insert point to first cell in inserted rows
|
|
if( !pCellForInsertPoint )
|
|
pCellForInsertPoint = pNewRow->GetFirstCell();
|
|
|
|
if( Y == iNewY )
|
|
pNewRow->InsertBefore(pCurrentRow);
|
|
else
|
|
{
|
|
pNewRow->InsertAfter(pCurrentRow);
|
|
pCurrentRow = pNewRow;
|
|
}
|
|
|
|
pNewRow->FinishedLoad(pBuffer);
|
|
}
|
|
|
|
// Pad the inserted rows or the table with extra cells
|
|
// to keep rectangular geometry
|
|
NormalizeCellsPerRow();
|
|
|
|
if( ppCellForInsertPoint && *ppCellForInsertPoint == NULL )
|
|
*ppCellForInsertPoint = pCellForInsertPoint;
|
|
}
|
|
|
|
// Now returns cell where we should move the insert point
|
|
void CEditTableElement::InsertColumns(int32 X, int32 iNewX, intn number,
|
|
CEditTableElement* pSourceTable, intn iStartRow,
|
|
CEditTableCellElement **ppCellForInsertPoint)
|
|
{
|
|
if ( number == 0 )
|
|
{
|
|
return; /* A waste of time, but legal. */
|
|
}
|
|
if ( number < 0 || X < 0 )
|
|
{
|
|
/* Illegal. */
|
|
XP_ASSERT(FALSE);
|
|
return;
|
|
}
|
|
CEditBuffer *pBuffer = GetEditBuffer();
|
|
XP_ASSERT(pBuffer);
|
|
|
|
CEditTableCellElement *pCellForInsertPoint = NULL;
|
|
if( pSourceTable )
|
|
pCellForInsertPoint = pSourceTable->GetFirstCell();
|
|
|
|
// Insert starts below top of table - pad source with blank
|
|
// rows
|
|
if( pSourceTable && iStartRow > 0 )
|
|
{
|
|
XP_ASSERT(pSourceTable->IsTable());
|
|
|
|
// Set flag to autoinsert space into new cells according to pref
|
|
pBuffer->SetFillNewCellWithSpace();
|
|
for( intn i = 0; i < iStartRow; i++ )
|
|
{
|
|
// Creating a row with "number" of cells
|
|
CEditTableRowElement* pRow = new CEditTableRowElement(number);
|
|
if( pRow )
|
|
{
|
|
pRow->InsertAsFirstChild(pSourceTable);
|
|
// Insert default space if pref is set
|
|
pRow->FinishedLoad(pBuffer);
|
|
}
|
|
}
|
|
pBuffer->ClearFillNewCellWithSpace();
|
|
}
|
|
|
|
CEditTableRowElement* pRow = GetFirstRow();
|
|
CEditTableRowElement* pLastRow = pRow;
|
|
CEditTableRowElement* pSourceRow = NULL;
|
|
while( pRow )
|
|
{
|
|
if( pSourceTable )
|
|
pSourceRow = pSourceTable->GetFirstRow();
|
|
|
|
pRow->InsertCells(X, iNewX, number, pSourceRow, &pCellForInsertPoint);
|
|
|
|
if( pSourceRow )
|
|
delete pSourceRow;
|
|
|
|
pRow = pRow->GetNextRow();
|
|
if( pRow )
|
|
pLastRow = pRow;
|
|
}
|
|
|
|
if( pSourceTable )
|
|
{
|
|
if( !pLastRow ) // Not Likely!
|
|
goto INSERT_COLUMNS_END;
|
|
|
|
// Do we have any more rows to paste?
|
|
pSourceRow = pSourceTable->GetFirstRow();
|
|
|
|
if( pSourceRow )
|
|
{
|
|
// The number of columns we will have to insert if we
|
|
// need to add rows at bottom of table
|
|
// so overflow from inserted columns line up
|
|
intn iColumnsBefore = GetColumnsSpanned(GetColumnX(0), iNewX);
|
|
|
|
while(pSourceRow)
|
|
{
|
|
CEditTableRowElement *pNextRow;
|
|
if( iColumnsBefore )
|
|
{
|
|
// We need cells before the insert column,
|
|
// so make a new row with blank cells
|
|
pNextRow = new CEditTableRowElement(iColumnsBefore);
|
|
if( pNextRow )
|
|
{
|
|
pNextRow->InsertAfter(pLastRow);
|
|
// Append the source cells to the the new row
|
|
pNextRow->AppendRow(pSourceRow);
|
|
}
|
|
} else {
|
|
// No extra columns needed,
|
|
// just append source row
|
|
pSourceRow->Unlink();
|
|
pSourceRow->InsertAfter(pLastRow);
|
|
pNextRow = pSourceRow;
|
|
}
|
|
|
|
XP_ASSERT(pNextRow);
|
|
if( pNextRow )
|
|
{
|
|
// Fill in rest of row with empty cells
|
|
// DOESN'T WORK WITH ROW NOT ALREADY LAYED OUT
|
|
//pNextRow->PadRowWithEmptyCells(iTotalColumns);
|
|
// Be sure any empty cells have required empty text elements
|
|
pNextRow->FinishedLoad(pBuffer);
|
|
}
|
|
pLastRow = pNextRow;
|
|
pSourceRow = pSourceTable->GetFirstRow();
|
|
}
|
|
}
|
|
}
|
|
NormalizeCellsPerRow();
|
|
|
|
INSERT_COLUMNS_END:
|
|
if( ppCellForInsertPoint && *ppCellForInsertPoint == NULL )
|
|
*ppCellForInsertPoint = pCellForInsertPoint;
|
|
}
|
|
|
|
XP_Bool CEditTableElement::ReplaceSpecialCells(CEditTableElement *pSourceTable, XP_Bool bIgnoreSourceLayout,
|
|
CEditTableCellElement **ppCellForInsertPoint )
|
|
{
|
|
CEditTableCellElement *pCellForInsertPoint = NULL;
|
|
CEditTableCellElement *pReplaceCell;
|
|
CEditTableCellElement *pSourceCell;
|
|
CEditTableCellElement *pNextReplaceCell;
|
|
CEditTableCellElement *pNextSourceCell;
|
|
intn iReplaceRow = 0;
|
|
intn iPrevReplaceRow = 0;
|
|
intn iSourceRow = 0;
|
|
intn iPrevSourceRow = 0;
|
|
EDT_TableCellData *pReplaceData;
|
|
EDT_TableCellData *pSourceData;
|
|
XP_Bool bAllSourceCellsPasted = TRUE;
|
|
|
|
if( !pSourceTable )
|
|
goto REPLACE_DONE;
|
|
|
|
pReplaceCell = GetFirstCell();
|
|
if( pReplaceCell )
|
|
{
|
|
// Skip to a special-selected cell
|
|
while( pReplaceCell && !pReplaceCell->IsSpecialSelected() )
|
|
pReplaceCell = pReplaceCell->GetNextCellInTable(&iReplaceRow);
|
|
}
|
|
pSourceCell = pSourceTable->GetFirstCell();
|
|
if( !pReplaceCell || !pSourceCell )
|
|
goto REPLACE_DONE;
|
|
|
|
iPrevReplaceRow = iReplaceRow;
|
|
|
|
do {
|
|
pReplaceData = pReplaceCell->GetData();
|
|
// Very, very unlikely to fail
|
|
if( !pReplaceData )
|
|
goto REPLACE_DONE;
|
|
|
|
// Note that we must get the csid of the current table
|
|
// because source cell is not part of doc yet
|
|
pSourceData = pSourceCell->GetData(GetWinCSID());
|
|
if( !pSourceData )
|
|
{
|
|
EDT_FreeTableCellData(pReplaceData);
|
|
goto REPLACE_DONE;
|
|
}
|
|
|
|
if( iReplaceRow != iPrevReplaceRow )
|
|
{
|
|
// We are on the next row to be replaced
|
|
iPrevReplaceRow = iReplaceRow;
|
|
// If cell layout of source and destination matches,
|
|
// we should get to the next source row at the same time,
|
|
// (which means iSourceRow should be 1 > than iPrevSourceRow)
|
|
// if iSourceRow wasn't increased, there's still source cells on the previous row
|
|
if( iSourceRow == iPrevSourceRow )
|
|
{
|
|
XP_TRACE(("CEditTableElement::ReplaceSpecialCells: More Source cells are left to paste."));
|
|
|
|
// We may flow cells into source, ignoring the source geometry
|
|
if( !bIgnoreSourceLayout )
|
|
{
|
|
// Skip over cells to get to the next source row
|
|
bAllSourceCellsPasted = FALSE;
|
|
while( pSourceCell && iSourceRow == iPrevSourceRow )
|
|
pSourceCell = pSourceCell->GetNextCellInTable(&iSourceRow);
|
|
if( !pSourceCell )
|
|
goto REPLACE_DONE;
|
|
}
|
|
iPrevSourceRow = iSourceRow;
|
|
}
|
|
}
|
|
else if( iSourceRow != iPrevSourceRow )
|
|
{
|
|
// We ran out of source cells on the desired row,
|
|
// but we still have cells to replace
|
|
XP_TRACE(("CEditTableElement::ReplaceSpecialCells: Not enough SOURCE cells to paste in this row."));
|
|
|
|
if( !bIgnoreSourceLayout )
|
|
{
|
|
// Skip to the next replace row (skip replacing rest on this row)
|
|
while( pReplaceCell && iReplaceRow == iPrevReplaceRow )
|
|
{
|
|
// Delete contents in the extra replace cells
|
|
if( pReplaceCell->IsSpecialSelected() )
|
|
pReplaceCell->DeleteContents();
|
|
|
|
pReplaceCell = pReplaceCell->GetNextCellInTable(&iReplaceRow);
|
|
}
|
|
|
|
// Skip to the next special selected cell
|
|
while( pReplaceCell && !pReplaceCell->IsSpecialSelected() )
|
|
pReplaceCell = pReplaceCell->GetNextCellInTable(&iReplaceRow);
|
|
if( !pReplaceCell )
|
|
break;
|
|
// We are now at the right place in the next replace row
|
|
iPrevReplaceRow = iReplaceRow;
|
|
}
|
|
}
|
|
// Be sure to do this here (not in above block)
|
|
// since we don't ever go there if cell layout
|
|
// of source and destination matches
|
|
iPrevSourceRow = iSourceRow;
|
|
|
|
// Must get these now before we move cells around
|
|
pNextSourceCell = pSourceCell->GetNextCellInTable(&iSourceRow);
|
|
pNextReplaceCell = pReplaceCell->GetNextCellInTable(&iReplaceRow);
|
|
|
|
// Skip to a special-selected cell
|
|
while( pNextReplaceCell && !pNextReplaceCell->IsSpecialSelected() )
|
|
pNextReplaceCell = pNextReplaceCell->GetNextCellInTable(&iReplaceRow);
|
|
|
|
// Move source cell to just after cell to be replaced
|
|
pSourceCell->Unlink();
|
|
pSourceCell->InsertAfter(pReplaceCell);
|
|
// then delete the replace cell
|
|
delete pReplaceCell;
|
|
|
|
// Use size data from the cell we replaced
|
|
// to minimize table layout distortion
|
|
// (pReplaceData is the "source" data)
|
|
pReplaceData->mask = CF_WIDTH | CF_HEIGHT | CF_COLSPAN | CF_ROWSPAN;
|
|
edt_CopyTableCellData(pSourceData, pReplaceData);
|
|
pSourceCell->SetData(pSourceData);
|
|
|
|
EDT_FreeTableCellData(pSourceData);
|
|
EDT_FreeTableCellData(pReplaceData);
|
|
|
|
// We will place caret in the first cell replaced
|
|
if( !pCellForInsertPoint )
|
|
pCellForInsertPoint = pSourceCell;
|
|
|
|
pReplaceCell = pNextReplaceCell;
|
|
pSourceCell = pNextSourceCell;
|
|
}
|
|
while( pReplaceCell && pSourceCell );
|
|
|
|
|
|
// Delete contents in the remaining replace cells
|
|
while( pReplaceCell )
|
|
{
|
|
if( pReplaceCell->IsSpecialSelected() )
|
|
pReplaceCell->DeleteContents();
|
|
|
|
pReplaceCell = pReplaceCell->GetNextCellInTable(&iReplaceRow);
|
|
}
|
|
|
|
// Check if we still have cells in last source row,
|
|
if( bAllSourceCellsPasted && pSourceCell )
|
|
bAllSourceCellsPasted = FALSE;
|
|
|
|
REPLACE_DONE:
|
|
if( ppCellForInsertPoint && *ppCellForInsertPoint == NULL )
|
|
*ppCellForInsertPoint = pCellForInsertPoint;
|
|
|
|
// Note: Caller deletes what remains of the source table
|
|
GetEditBuffer()->ClearSpecialCellSelection();
|
|
return bAllSourceCellsPasted;
|
|
}
|
|
|
|
// Code moved from CDeleteTableCommand::CDeleteTableCommand,
|
|
// Note: This version breaks the older, now unused Undo system
|
|
void CEditTableElement::Delete(XP_Bool bMoveInsertPoint)
|
|
{
|
|
CEditBuffer *pBuffer = GetEditBuffer();
|
|
if( !pBuffer )
|
|
return;
|
|
CEditElement* pRefreshStart = GetFirstMostChild()->PreviousLeaf();
|
|
CEditInsertPoint replacePoint(GetLastMostChild()->NextLeaf(), 0);
|
|
// Move only if requested to
|
|
// (This is used when pasting a table,
|
|
// which inserts and moves insert point BEFORE deleting table)
|
|
if( bMoveInsertPoint )
|
|
pBuffer->SetInsertPoint(replacePoint);
|
|
|
|
// Be sure to unselect the table if it is selected
|
|
// else Relayout blows up
|
|
if( pBuffer->GetSelectedTable() == this )
|
|
pBuffer->SelectTable(FALSE);
|
|
|
|
Unlink();
|
|
|
|
// Left over from older Undo system
|
|
//m_replacePoint = pBuffer()->EphemeralToPersistent(replacePoint);
|
|
|
|
pBuffer->Relayout(pRefreshStart, 0, replacePoint.m_pElement);
|
|
|
|
// Delete ourselves
|
|
delete this;
|
|
}
|
|
|
|
// Note: no saving to pUndoContainer any more
|
|
void CEditTableElement::DeleteRows(int32 Y, intn number,
|
|
CEditTableCellElement **ppCellForInsertPoint)
|
|
{
|
|
if ( number == 0 ) {
|
|
return; /* A waste of time, but legal. */
|
|
}
|
|
if ( number < 0 || Y < 0 ){
|
|
/* Illegal. */
|
|
XP_ASSERT(FALSE);
|
|
return;
|
|
}
|
|
|
|
intn iRowIndex = GetRowIndex(Y);
|
|
// Abort if invalid Y value
|
|
if( iRowIndex == -1 )
|
|
return;
|
|
|
|
CEditTableCellElement *pCellForInsertPoint = NULL;
|
|
|
|
// Restrict number of rows we can delete
|
|
intn iLastIndex = min(iRowIndex + number - 1, m_iRows-1);
|
|
CEditTableRowElement *pRowAbove = NULL;
|
|
CEditTableRowElement *pRowBelow = NULL;
|
|
if( iRowIndex > 0 )
|
|
pRowAbove = GetRow(GetRowY(iRowIndex-1));
|
|
|
|
for( ; iRowIndex <= iLastIndex; iRowIndex++ )
|
|
{
|
|
Y = GetRowY(iRowIndex);
|
|
CEditTableRowElement *pRow = GetRow(Y);
|
|
|
|
// Get first cell starting at or spanning the Y value (TRUE)
|
|
CEditTableCellElement *pCell = GetFirstCellInRow(Y, TRUE);
|
|
|
|
while( pCell )
|
|
{
|
|
// Get next cell before we delete the one we're at
|
|
CEditTableCellElement *pNextCell = GetNextCellInRow();
|
|
|
|
if( Y == pCell->GetY() )
|
|
{
|
|
// Cell is in same row as delete row
|
|
// Unselect if selected and delete the cell
|
|
if( pCell->IsSelected() )
|
|
GetEditBuffer()->SelectCell(FALSE, NULL, pCell);
|
|
|
|
delete pCell;
|
|
// Note: We don't worry about deleting cells with rowspan > 1
|
|
// even though that might upset the column layout for cells
|
|
// below the deleted rows.
|
|
// We might want to revisit this later -- we would have to
|
|
// insert blank cells into rows below when rowspan > number
|
|
// of rows we are deleting
|
|
}
|
|
else if( pCell->GetRowSpan() > 1 )
|
|
{
|
|
// Cell spans the delete row
|
|
// (its actually in a row above the delete row)
|
|
// Reduce its rowspan instead of deleting
|
|
pCell->DecreaseRowSpan(1);
|
|
}
|
|
#ifdef DEBUG
|
|
else
|
|
XP_TRACE(("CEditTableElement::DeleteRows: Rowspan doesnt agree with rowlayout"));
|
|
#endif
|
|
pCell = pNextCell;
|
|
}
|
|
// Delete the row if all cells were deleted (should always be true?)
|
|
// VERY IMPORTANT. Never call anything to cause
|
|
// relayout else our m_pRowLayoutData and entire coordinate
|
|
// system will be altered
|
|
if( pRow && pRow->GetChild() == NULL )
|
|
{
|
|
// Get the next row if available before deleting this one
|
|
pRowBelow = pRow->GetNextRow();
|
|
#ifdef DEBUG
|
|
if( pRowBelow )
|
|
XP_ASSERT(pRowBelow->IsTableRow());
|
|
#endif
|
|
delete pRow;
|
|
}
|
|
}
|
|
// Get first cell in the next row after deleted rows,
|
|
// or row before if we deleted to the end of the table
|
|
if( pRowBelow )
|
|
pCellForInsertPoint = pRowBelow->GetFirstCell();
|
|
else if( pRowAbove )
|
|
pCellForInsertPoint = pRowAbove->GetFirstCell();
|
|
|
|
if( ppCellForInsertPoint && *ppCellForInsertPoint == NULL )
|
|
*ppCellForInsertPoint = pCellForInsertPoint;
|
|
}
|
|
|
|
void CEditTableElement::DeleteColumns(int32 X, intn number, CEditTableCellElement **ppCellForInsertPoint)
|
|
{
|
|
if ( number == 0 )
|
|
{
|
|
return; /* A waste of time, but legal. */
|
|
}
|
|
if ( number < 0 || X < 0 )
|
|
{
|
|
/* Illegal. */
|
|
XP_ASSERT(FALSE);
|
|
return;
|
|
}
|
|
CEditTableCellElement *pCellForInsertPoint = NULL;
|
|
|
|
CEditTableRowElement* pRow = GetFirstRow();
|
|
|
|
while( pRow )
|
|
{
|
|
// Delete cells from the row and save the first cell found for an insert point
|
|
pRow->DeleteCells(X, number, &pCellForInsertPoint);
|
|
|
|
CEditTableRowElement *pNextRow = pRow->GetNextRow();
|
|
|
|
// Check if ALL cells were deleted - delete row if all were
|
|
if( !pRow->GetChild() )
|
|
delete pRow;
|
|
|
|
pRow = pNextRow;
|
|
}
|
|
if( ppCellForInsertPoint && *ppCellForInsertPoint == NULL )
|
|
*ppCellForInsertPoint = pCellForInsertPoint;
|
|
}
|
|
|
|
// Returns TRUE if any row was deleted
|
|
// Return the last cell in row above the first deleted row
|
|
// if first row wasn't deleted
|
|
XP_Bool CEditTableElement::DeleteEmptyRows(CEditLeafElement **ppPreviousLeaf)
|
|
{
|
|
int32 iRowsInTable = m_iRows;
|
|
CEditTableRowElement *pRow = GetFirstRow();
|
|
CEditTableRowElement *pPrevRow = 0;
|
|
int32 iCount = 0;
|
|
int32 iIndex = 0;
|
|
int32 iFirstEmptyRowY = -1;
|
|
CEditTableCellElement *pCellForInsertPoint = NULL;
|
|
// Clear this now in case we don't find a valid leaf to use
|
|
if( ppPreviousLeaf )
|
|
*ppPreviousLeaf = 0;
|
|
|
|
// Count number of completely empty rows
|
|
while( pRow )
|
|
{
|
|
if( pRow->AllCellsInRowAreEmpty() )
|
|
{
|
|
if( iFirstEmptyRowY == -1 )
|
|
iFirstEmptyRowY = GetRowY(iIndex);
|
|
iCount++;
|
|
|
|
if( iCount == 1 && pPrevRow && ppPreviousLeaf )
|
|
{
|
|
// We are in the first row we will delete and it isn't the first row of table
|
|
// Get the last cell in the previous row
|
|
CEditTableCellElement *pCell = pPrevRow->GetFirstCell();
|
|
CEditTableCellElement *pNextCell;
|
|
while( pCell && (pNextCell = GetNextCellInRow(pCell)) != 0 )
|
|
pCell = pNextCell;
|
|
|
|
// Return last leaf in that cell
|
|
if( pCell )
|
|
{
|
|
CEditElement *pLast = pCell->GetLastMostChild();
|
|
while( pLast && (!pLast->IsLeaf() || pLast->Leaf()->GetLen() == 0) )
|
|
pLast = pLast->GetPreviousSibling();
|
|
if( pLast && pLast->IsLeaf() )
|
|
*ppPreviousLeaf = pLast->Leaf();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pPrevRow = pRow;
|
|
}
|
|
|
|
iIndex++;
|
|
pRow = pRow->GetNextRow();
|
|
}
|
|
if( iCount > 0 )
|
|
{
|
|
DeleteRows(iFirstEmptyRowY, iCount);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// This is the old version - just finds row based on counting,
|
|
// no ROWSPAN effect needed
|
|
CEditTableRowElement* CEditTableElement::FindRow(intn number)
|
|
{
|
|
intn count = 0;
|
|
if ( number < 0 )
|
|
{
|
|
XP_ASSERT(FALSE);
|
|
return NULL;
|
|
}
|
|
for ( CEditElement* pChild = GetChild();
|
|
pChild;
|
|
pChild = pChild->GetNextSibling())
|
|
{
|
|
if ( pChild->IsTableRow() )
|
|
{
|
|
if ( count++ == number )
|
|
{
|
|
return (CEditTableRowElement*) pChild;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
CEditCaptionElement* CEditTableElement::GetCaption(){
|
|
// Normally first or last, but we check everybody to be robust.
|
|
for ( CEditElement* pChild = GetChild();
|
|
pChild;
|
|
pChild = pChild->GetNextSibling()) {
|
|
if ( pChild->IsCaption() ) {
|
|
return (CEditCaptionElement*) pChild;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void CEditTableElement::SetCaption(CEditCaptionElement* pCaption){
|
|
DeleteCaption();
|
|
if ( pCaption ){
|
|
EDT_TableCaptionData* pData = pCaption->GetData(GetWinCSID());
|
|
XP_Bool bAddAtTop = ! ( pData->align == ED_ALIGN_BOTTOM || pData->align == ED_ALIGN_ABSBOTTOM);
|
|
CEditCaptionElement::FreeData(pData);
|
|
if ( bAddAtTop ){
|
|
pCaption->InsertAsFirstChild(this);
|
|
}
|
|
else {
|
|
pCaption->InsertAsLastChild(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CEditTableElement::DeleteCaption(){
|
|
CEditCaptionElement* pOldCaption = GetCaption();
|
|
delete pOldCaption;
|
|
}
|
|
|
|
void CEditTableElement::DeleteCaptionAbove()
|
|
{
|
|
CEditCaptionElement* pCaption = GetCaption();
|
|
// A caption "above" the table is always the first
|
|
// child of the table
|
|
if( pCaption && pCaption == GetChild() )
|
|
delete pCaption;
|
|
}
|
|
|
|
void CEditTableElement::DeleteCaptionBelow()
|
|
{
|
|
CEditCaptionElement* pCaption = GetCaption();
|
|
// A caption "below" the table is always the
|
|
// last of the table. Its simpler to just check
|
|
// if its not the first child
|
|
if( pCaption && pCaption != GetChild() )
|
|
delete pCaption;
|
|
}
|
|
|
|
void CEditTableElement::FinishedLoad( CEditBuffer* pBuffer ){
|
|
// From experimentation, we know that the 2.0 navigator dumps
|
|
// tags that aren't in td, tr, or caption cells into the doc
|
|
// before the table.
|
|
//
|
|
// That's a little too radical for me. So what I do is
|
|
// wrap tds in trs, put anything else
|
|
// into a caption
|
|
|
|
CEditCaptionElement* pCaption = NULL;
|
|
CEditTableRowElement* pRow = NULL;
|
|
CEditElement* pNext = 0;
|
|
|
|
// TODO: MAKE THIS WORK BEFORE LAYOUT HAPPENS (no m_pColumnLayoutData yet)
|
|
//FixupColumnsAndRows();
|
|
|
|
// For efficiency, count rows only
|
|
// if we haven't layed out table
|
|
if( m_iRows <= 0 )
|
|
m_iRows = CountRows();
|
|
|
|
if ( m_iRows <= 0 ) {
|
|
// Force a row
|
|
CEditTableRowElement* pTempRow = new CEditTableRowElement();
|
|
pTempRow->InsertAsFirstChild(this);
|
|
}
|
|
|
|
CEditElement* pChild;
|
|
for ( pChild = GetChild();
|
|
pChild;
|
|
pChild = pNext) {
|
|
pNext = pChild->GetNextSibling(); // We might unlink pChild
|
|
if ( ! IsAcceptableChild(*pChild) ){
|
|
if ( pChild->IsTableCell() ){
|
|
if ( ! pRow ){
|
|
pRow = new CEditTableRowElement();
|
|
pRow->InsertAfter(pChild);
|
|
}
|
|
pChild->Unlink();
|
|
pChild->InsertAsLastChild(pRow);
|
|
if ( pCaption ){
|
|
pCaption->FinishedLoad(pBuffer);
|
|
pCaption = NULL;
|
|
}
|
|
}
|
|
else {
|
|
// Put random children into a caption
|
|
if ( ! pCaption ){
|
|
pCaption = new CEditCaptionElement();
|
|
pCaption->InsertAfter(pChild);
|
|
}
|
|
pChild->Unlink();
|
|
pChild->InsertAsLastChild(pCaption);
|
|
if ( pRow ){
|
|
pRow->FinishedLoad(pBuffer);
|
|
pRow = NULL;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if ( pRow ){
|
|
pRow->FinishedLoad(pBuffer);
|
|
pRow = NULL;
|
|
}
|
|
if ( pCaption ){
|
|
pCaption->FinishedLoad(pBuffer);
|
|
pCaption = NULL;
|
|
}
|
|
}
|
|
pChild->FinishedLoad(pBuffer);
|
|
}
|
|
if ( pRow ){
|
|
pRow->FinishedLoad(pBuffer);
|
|
pRow = NULL;
|
|
}
|
|
if ( pCaption ){
|
|
pCaption->FinishedLoad(pBuffer);
|
|
pCaption = NULL;
|
|
}
|
|
|
|
// Merge all captions together.
|
|
pCaption = NULL;
|
|
for ( pChild = GetChild();
|
|
pChild;
|
|
pChild = pNext) {
|
|
pNext = pChild->GetNextSibling(); // We might unlink pChild
|
|
if ( pChild->IsCaption() ){
|
|
if ( pCaption ) {
|
|
// This is an extra caption. Merge its contents into the other caption.
|
|
pChild->Unlink();
|
|
CEditElement* pItem;
|
|
while ( ( pItem = pChild->GetChild() ) != NULL ){
|
|
pItem->Unlink();
|
|
pItem->InsertAsLastChild(pCaption);
|
|
}
|
|
delete pChild;
|
|
}
|
|
else {
|
|
pCaption = (CEditCaptionElement*) pChild;
|
|
}
|
|
}
|
|
}
|
|
AdjustCaption();
|
|
|
|
EnsureSelectableSiblings(pBuffer);
|
|
}
|
|
|
|
XP_Bool CEditTableElement::MoveCaptionOutsideTable(XP_Bool bAfter)
|
|
{
|
|
CEditCaptionElement *pCaption = GetCaption();
|
|
XP_Bool bReturn = FALSE;
|
|
|
|
if ( pCaption )
|
|
{
|
|
CEditContainerElement *pContainer = (CEditContainerElement*)pCaption->GetChild();
|
|
// Skip over the division element used for alignment
|
|
// (Alternate method is to start at first leaf and find its container,
|
|
// but that would miss lists, so lets try to start from top down)
|
|
if( pContainer && pContainer->IsDivision() )
|
|
pContainer = (CEditContainerElement*)pContainer->GetChild();
|
|
|
|
if( !pContainer || !pContainer->IsContainer() ||
|
|
(pContainer->IsEmpty() && pContainer->GetNextSibling() == 0) )
|
|
return FALSE;
|
|
|
|
CEditElement *pLastChild = GetLastMostChild();
|
|
CEditLeafElement *pBefore = bAfter ? 0 : PreviousLeaf();
|
|
CEditLeafElement *pAfter = (bAfter && pLastChild) ? pLastChild->NextLeaf() : 0;
|
|
CEditContainerElement *pOutsideContainer;
|
|
// Save container after current so all are moved
|
|
CEditElement *pNextContainer = pContainer->GetNextSibling();
|
|
|
|
if( pBefore )
|
|
{
|
|
pOutsideContainer = pBefore->FindContainer();
|
|
if( pOutsideContainer )
|
|
{
|
|
// Move the first container found
|
|
pContainer->Unlink();
|
|
pContainer->InsertAfter(pOutsideContainer);
|
|
bReturn = TRUE;
|
|
}
|
|
}
|
|
else if( pAfter )
|
|
{
|
|
pOutsideContainer = pAfter->FindContainer();
|
|
if( pOutsideContainer )
|
|
{
|
|
pContainer->Unlink();
|
|
pContainer->InsertBefore(pOutsideContainer);
|
|
bReturn = TRUE;
|
|
}
|
|
}
|
|
if( bReturn && pNextContainer != 0 )
|
|
{
|
|
// Move any other containers following the one just moved
|
|
CEditElement *pInsertAfter = pContainer;
|
|
while( pNextContainer )
|
|
{
|
|
pContainer = (CEditContainerElement*)pNextContainer;
|
|
pNextContainer = pContainer->GetNextSibling();
|
|
pContainer->Unlink();
|
|
pContainer->InsertAfter(pInsertAfter);
|
|
pInsertAfter = pContainer;
|
|
}
|
|
}
|
|
}
|
|
return bReturn;
|
|
}
|
|
|
|
void CEditTableElement::AdjustCaption() {
|
|
CEditCaptionElement* pCaption = GetCaption();
|
|
if ( pCaption ) {
|
|
// Now move the caption to the start or the end of the table, as appropriate.
|
|
pCaption->Unlink();
|
|
SetCaption(pCaption);
|
|
}
|
|
}
|
|
|
|
void CEditTableElement::StreamOut( IStreamOut *pOut){
|
|
CEditElement::StreamOut( pOut );
|
|
pOut->WriteInt( (int32)m_align );
|
|
pOut->WriteInt( (int32)m_malign );
|
|
}
|
|
|
|
void CEditTableElement::SetData( EDT_TableData *pData ){
|
|
char *pNew = CreateTagData(pData, FALSE);
|
|
CEditElement::SetTagData( pNew );
|
|
m_align = pData->align;
|
|
m_malign = pData->malign; // Keep track of alignment seperately to avoid crashing when editing.
|
|
if ( pNew ) {
|
|
free(pNew); // XP_FREE?
|
|
}
|
|
m_iWidthPixels = pData->iWidthPixels;
|
|
m_iWidth = pData->iWidth;
|
|
m_bWidthPercent = pData->bWidthPercent;
|
|
|
|
m_iHeightPixels = pData->iHeightPixels;
|
|
m_iHeight = pData->iHeight;
|
|
m_bHeightPercent = pData->bHeightPercent;
|
|
|
|
m_iCellSpacing = pData->iCellSpacing;
|
|
m_iCellPadding = pData->iCellPadding;
|
|
if( pData->bBorderWidthDefined && pData->iBorderWidth > 0 )
|
|
m_iCellBorder = 1;
|
|
else
|
|
m_iCellBorder = 0;
|
|
|
|
m_iRows = pData->iRows;
|
|
m_iColumns = pData->iColumns;
|
|
}
|
|
|
|
char* CEditTableElement::CreateTagData(EDT_TableData *pData, XP_Bool bPrinting) {
|
|
char *pNew = 0;
|
|
m_align = pData->align;
|
|
m_malign = pData->malign;
|
|
if( bPrinting && pData->malign != ED_ALIGN_DEFAULT ){
|
|
pNew = PR_sprintf_append( pNew, "ALIGN=%s ", EDT_AlignString(pData->malign) );
|
|
}
|
|
// If you don't mention the border, you get a zero pixel border.
|
|
// If you just say BORDER, it's like BORDER=1
|
|
if ( pData->bBorderWidthDefined ){
|
|
if ( pData->iBorderWidth == 1 ) {
|
|
/* Neatness counts. */
|
|
pNew = PR_sprintf_append( pNew, "BORDER ");
|
|
}
|
|
else {
|
|
pNew = PR_sprintf_append( pNew, "BORDER=%d ", pData->iBorderWidth );
|
|
}
|
|
}
|
|
if ( pData->iCellSpacing != 1 ){
|
|
pNew = PR_sprintf_append( pNew, "CELLSPACING=%d ", pData->iCellSpacing );
|
|
}
|
|
if ( pData->iCellPadding != 1 ){
|
|
pNew = PR_sprintf_append( pNew, "CELLPADDING=%d ", pData->iCellPadding );
|
|
}
|
|
if ( pData->bUseCols ){
|
|
int cols = (int) GetColumns(); //Should we use CountColumns()?
|
|
if ( cols == 0 ) {
|
|
cols = (int) pData->iColumns;
|
|
}
|
|
pNew = PR_sprintf_append( pNew, "COLS=%d ", cols );
|
|
}
|
|
if( pData->bWidthDefined ){
|
|
if( pData->bWidthPercent ){
|
|
pNew = PR_sprintf_append( pNew, "WIDTH=\"%ld%%\" ",
|
|
(long)min(100,max(1,pData->iWidth)) );
|
|
}
|
|
else {
|
|
pNew = PR_sprintf_append( pNew, "WIDTH=\"%ld\" ", (long)pData->iWidth );
|
|
}
|
|
}
|
|
if( pData->bHeightDefined ){
|
|
if( pData->bHeightPercent ){
|
|
pNew = PR_sprintf_append( pNew, "HEIGHT=\"%ld%%\" ",
|
|
(long)min(100,max(1, pData->iHeight)) );
|
|
}
|
|
else {
|
|
pNew = PR_sprintf_append( pNew, "HEIGHT=\"%ld\" ", (long)pData->iHeight );
|
|
}
|
|
}
|
|
if ( pData->pColorBackground ) {
|
|
SetBackgroundColor(EDT_LO_COLOR(pData->pColorBackground));
|
|
pNew = PR_sprintf_append( pNew, "BGCOLOR=\"#%06lX\" ", GetBackgroundColor().GetAsLong() );
|
|
}
|
|
else {
|
|
SetBackgroundColor(ED_Color::GetUndefined());
|
|
}
|
|
if ( pData->pBackgroundImage ) {
|
|
SetBackgroundImage(pData->pBackgroundImage);
|
|
pNew = PR_sprintf_append( pNew, "BACKGROUND=\"%s\" ", pData->pBackgroundImage );
|
|
}
|
|
else {
|
|
SetBackgroundImage(0);
|
|
}
|
|
if ( pData->bBackgroundNoSave) {
|
|
pNew = PR_sprintf_append( pNew, "NOSAVE " );
|
|
}
|
|
|
|
if( pData->pExtra ){
|
|
pNew = PR_sprintf_append( pNew, "%s ", pData->pExtra );
|
|
}
|
|
|
|
if( pNew ){
|
|
pNew = PR_sprintf_append( pNew, ">" );
|
|
}
|
|
return pNew;
|
|
}
|
|
|
|
EDT_TableData* CEditTableElement::GetData()
|
|
{
|
|
EDT_TableData *pRet;
|
|
PA_Tag* pTag = CEditElement::TagOpen(0);
|
|
|
|
pRet = ParseParams( pTag, GetWinCSID() );
|
|
// Alignment isn't kept in the tag data, because tag data is used for editing,
|
|
// and we crash if we try to edit an aligned table, because layout doesn't
|
|
// set up the data structures that we need.
|
|
pRet->align = m_align;
|
|
pRet->malign = m_malign;
|
|
// Return the widths determined by layout
|
|
pRet->iWidth = m_iWidth;
|
|
pRet->iWidthPixels = m_iWidthPixels;
|
|
|
|
pRet->iHeight = m_iHeight;
|
|
pRet->iHeightPixels = m_iHeightPixels;
|
|
|
|
pRet->iCellSpacing = m_iCellSpacing;
|
|
pRet->iCellPadding = m_iCellPadding;
|
|
pRet->iRows = m_iRows;
|
|
pRet->iColumns = m_iColumns;
|
|
PA_FreeTag( pTag );
|
|
return pRet;
|
|
}
|
|
|
|
static char *tableParams[] = {
|
|
PARAM_ALIGN,
|
|
PARAM_BORDER,
|
|
PARAM_CELLSPACE,
|
|
PARAM_CELLPAD,
|
|
PARAM_COLS,
|
|
PARAM_WIDTH,
|
|
PARAM_HEIGHT,
|
|
PARAM_BGCOLOR,
|
|
PARAM_BACKGROUND,
|
|
PARAM_NOSAVE,
|
|
0
|
|
};
|
|
|
|
EDT_TableData* CEditTableElement::ParseParams( PA_Tag *pTag, int16 csid ){
|
|
EDT_TableData *pData = NewData();
|
|
ED_Alignment malign;
|
|
|
|
malign = edt_FetchParamAlignment( pTag, ED_ALIGN_DEFAULT, FALSE, csid );
|
|
// Center not supported in 3.0
|
|
if( malign == ED_ALIGN_RIGHT || malign == ED_ALIGN_LEFT || malign == ED_ALIGN_DEFAULT ){
|
|
pData->malign = malign;
|
|
}
|
|
|
|
pData->iRows = GetRows(); // Should we use CountRows()? NO THIS IS CALLED EVERY GetData call
|
|
pData->iColumns = GetColumns(); // Should we use CountColumns()?
|
|
|
|
// If you just say BORDER, it's the same as BORDER=1
|
|
// If you say BORDER=0, it's different than not saying BORDER at all
|
|
pData->bBorderWidthDefined = edt_FetchParamBoolExist(pTag, PARAM_BORDER, csid);
|
|
pData->iBorderWidth = edt_FetchParamInt(pTag, PARAM_BORDER, 0, 1, csid);
|
|
pData->iCellSpacing = edt_FetchParamInt(pTag, PARAM_CELLSPACE, 1, csid);
|
|
pData->iCellPadding = edt_FetchParamInt(pTag, PARAM_CELLPAD, 1, csid);
|
|
{
|
|
// If we're parsing a table tag, and we've hit a cols= property, we
|
|
// need to remember the number of columns.
|
|
int columns = edt_FetchParamInt(pTag, PARAM_COLS, 0, csid);
|
|
pData->bUseCols = columns > 0;
|
|
if ( pData->bUseCols ) {
|
|
pData->iColumns = columns;
|
|
}
|
|
}
|
|
pData->bWidthDefined = edt_FetchDimension( pTag, PARAM_WIDTH,
|
|
&pData->iWidth, &pData->bWidthPercent,
|
|
100, TRUE, csid );
|
|
pData->bHeightDefined = edt_FetchDimension( pTag, PARAM_HEIGHT,
|
|
&pData->iHeight, &pData->bHeightPercent,
|
|
100, TRUE, csid );
|
|
|
|
// If width and/or height are NOT defined, use the
|
|
// "bPercent" values set by any previous SetData() calls
|
|
if( !pData->bWidthDefined )
|
|
pData->bWidthPercent = m_bWidthPercent;
|
|
if( !pData->bHeightDefined )
|
|
pData->bHeightPercent = m_bHeightPercent;
|
|
|
|
|
|
pData->pColorBackground = edt_MakeLoColor(edt_FetchParamColor( pTag, PARAM_BGCOLOR, csid ));
|
|
pData->pBackgroundImage = edt_FetchParamString( pTag, PARAM_BACKGROUND, csid );
|
|
pData->bBackgroundNoSave = edt_FetchParamBoolExist( pTag, PARAM_NOSAVE, csid );
|
|
pData->pExtra = edt_FetchParamExtras( pTag, tableParams, csid );
|
|
|
|
return pData;
|
|
}
|
|
|
|
EDT_TableData* CEditTableElement::NewData(){
|
|
EDT_TableData *pData = XP_NEW( EDT_TableData );
|
|
if( pData == 0 ){
|
|
// throw();
|
|
return pData;
|
|
}
|
|
pData->align = ED_ALIGN_LEFT;
|
|
pData->malign = ED_ALIGN_DEFAULT;
|
|
// While this helps layout, it causes lots of problems
|
|
// in layout when COLSPAN > 1 an ROWSPAN > 1
|
|
pData->bUseCols = FALSE;
|
|
pData->iRows = 1;
|
|
pData->iColumns = 1;
|
|
pData->bBorderWidthDefined = TRUE;
|
|
pData->iBorderWidth = ED_DEFAULT_TABLE_BORDER;
|
|
pData->iCellSpacing = ED_DEFAULT_CELL_SPACING;
|
|
pData->iCellPadding = ED_DEFAULT_CELL_PADDING;
|
|
pData->bWidthDefined = FALSE;
|
|
pData->bWidthPercent = FALSE; // Percent mode is a pain
|
|
pData->iWidth = 1;
|
|
pData->iWidthPixels = 1;
|
|
pData->bHeightDefined = FALSE;
|
|
pData->bHeightPercent = FALSE;
|
|
pData->iHeight = 1;
|
|
pData->iHeightPixels = 1;
|
|
pData->pColorBackground = 0;
|
|
pData->pBackgroundImage = 0;
|
|
pData->bBackgroundNoSave = FALSE;
|
|
pData->pExtra = 0;
|
|
return pData;
|
|
}
|
|
|
|
void CEditTableElement::FreeData( EDT_TableData *pData ){
|
|
if( pData->pColorBackground ) XP_FREE( pData->pColorBackground );
|
|
if( pData->pBackgroundImage ) XP_FREE( pData->pBackgroundImage );
|
|
if( pData->pExtra ) XP_FREE( pData->pExtra );
|
|
XP_FREE( pData );
|
|
}
|
|
|
|
void CEditTableElement::SetBackgroundColor( ED_Color iColor ){
|
|
m_backgroundColor = iColor;
|
|
}
|
|
|
|
ED_Color CEditTableElement::GetBackgroundColor(){
|
|
return m_backgroundColor;
|
|
}
|
|
|
|
void CEditTableElement::SetBackgroundImage( char* pImage ){
|
|
delete m_pBackgroundImage;
|
|
m_pBackgroundImage = 0;
|
|
if ( pImage ) {
|
|
m_pBackgroundImage = XP_STRDUP(pImage);
|
|
}
|
|
}
|
|
|
|
char* CEditTableElement::GetBackgroundImage(){
|
|
return m_pBackgroundImage;
|
|
}
|
|
|
|
void CEditTableElement::PrintOpen( CPrintState *pPrintState ){
|
|
pPrintState->m_iCharPos = 0;
|
|
pPrintState->m_pOut->Printf( "\n");
|
|
|
|
PA_Tag *pTag = InternalTagOpen(0, TRUE);
|
|
while( pTag ){
|
|
char *pData = 0;
|
|
if( pTag->data ){
|
|
PA_LOCK( pData, char*, pTag->data );
|
|
}
|
|
|
|
if( pData && *pData != '>' ) {
|
|
pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "<%s %s",
|
|
EDT_TagString(pTag->type), pData );
|
|
}
|
|
else {
|
|
pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "<%s>",
|
|
EDT_TagString(pTag->type));
|
|
}
|
|
|
|
if( pTag->data ){
|
|
PA_UNLOCK( pTag->data );
|
|
}
|
|
|
|
PA_Tag* pDelTag = pTag;
|
|
pTag = pTag->next;
|
|
PA_FreeTag(pDelTag);
|
|
}
|
|
}
|
|
|
|
void CEditTableElement::PrintEnd( CPrintState *pPrintState ){
|
|
PA_Tag *pTag = TagEnd();
|
|
while( pTag ){
|
|
char *pData = 0;
|
|
if( pTag->data ){
|
|
PA_LOCK( pData, char*, pTag->data );
|
|
}
|
|
|
|
pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "</%s>",
|
|
EDT_TagString( pTag->type ) );
|
|
|
|
if( pTag->data ){
|
|
PA_UNLOCK( pTag->data );
|
|
}
|
|
|
|
PA_Tag* pDelTag = pTag;
|
|
pTag = pTag->next;
|
|
PA_FreeTag(pDelTag);
|
|
}
|
|
|
|
pPrintState->m_iCharPos = 0;
|
|
pPrintState->m_pOut->Printf( "\n");
|
|
}
|
|
|
|
LO_TableStruct* CEditTableElement::GetLoTable()
|
|
{
|
|
// Find the first leaf element in the table since
|
|
// it will have pointer to LO_Element
|
|
CEditElement *pLeaf = FindNextElement(&CEditElement::FindLeafAll,0 );
|
|
if( pLeaf )
|
|
{
|
|
// Only leaf edit elements have their pointers saved in LO_Elements
|
|
LO_Element *pLoElement = pLeaf->Leaf()->GetLayoutElement();
|
|
if( pLoElement )
|
|
{
|
|
// Find enclosing LO cell
|
|
LO_TableStruct *pLoTable = lo_GetParentTable(GetEditBuffer()->m_pContext, pLoElement);
|
|
// We shoul ALWAYS find a table
|
|
XP_ASSERT(pLoTable);
|
|
return pLoTable;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void CEditTableElement::GetParentSize(MWContext *pContext, int32 *pWidth, int32 *pHeight, LO_TableStruct *pLoTable)
|
|
{
|
|
// This may be supplied -- get it if not
|
|
if( !pLoTable )
|
|
pLoTable = GetLoTable();
|
|
|
|
int32 iParentWidth = 1;
|
|
int32 iParentHeight = 1;
|
|
|
|
if( pLoTable )
|
|
{
|
|
// Get viewing window
|
|
int32 iXOrigin, iYOrigin;
|
|
FE_GetDocAndWindowPosition(pContext, &iXOrigin, &iYOrigin, &iParentWidth, &iParentHeight);
|
|
|
|
// Get extra margin
|
|
int32 iMarginWidth;
|
|
int32 iMarginHeight;
|
|
LO_GetDocumentMargins(pContext, &iMarginWidth, &iMarginHeight);
|
|
|
|
// Check for embeded table - get max width from Containing cell
|
|
LO_CellStruct *pParentCell = lo_GetParentCell( pContext, (LO_Element*)pLoTable );
|
|
|
|
if( pParentCell )
|
|
{
|
|
iParentWidth = pParentCell->width - (2 * pParentCell->border_horiz_space);
|
|
iParentHeight = pParentCell->height - (2 * pParentCell->border_vert_space);
|
|
} else {
|
|
iParentWidth -= 2 * iMarginWidth;
|
|
iParentHeight -= 2 * iMarginHeight;
|
|
}
|
|
}
|
|
if( pWidth )
|
|
*pWidth = iParentWidth;
|
|
|
|
if( pHeight )
|
|
*pHeight = iParentHeight;
|
|
}
|
|
|
|
void CEditTableElement::SetSizeMode(MWContext *pContext, int iMode)
|
|
{
|
|
int32 iParentWidth, iParentHeight;
|
|
XP_Bool bCellPercent = (iMode & ED_MODE_CELL_PERCENT) ? TRUE : FALSE;
|
|
XP_Bool bTablePercent = (iMode & ED_MODE_TABLE_PERCENT) ? TRUE : FALSE;
|
|
XP_Bool bCellPixels = (iMode & ED_MODE_CELL_PIXELS) ? TRUE : FALSE;
|
|
XP_Bool bTablePixels = (iMode & ED_MODE_TABLE_PIXELS) ? TRUE : FALSE;
|
|
XP_Bool bUseCols = (iMode & ED_MODE_USE_COLS) ? TRUE : FALSE;
|
|
XP_Bool bNoCols = (iMode & ED_MODE_NO_COLS) ? TRUE : FALSE;
|
|
XP_Bool bChanged = FALSE;
|
|
XP_Bool bSetTableWidth = (iMode & ED_MODE_USE_TABLE_WIDTH) ? TRUE : FALSE;
|
|
XP_Bool bClearTableWidth = (iMode & ED_MODE_NO_TABLE_WIDTH) ? TRUE : FALSE;
|
|
XP_Bool bSetTableHeight = (iMode & ED_MODE_USE_TABLE_HEIGHT) ? TRUE : FALSE;
|
|
XP_Bool bClearTableHeight = (iMode & ED_MODE_NO_TABLE_HEIGHT) ? TRUE : FALSE;
|
|
|
|
XP_Bool bSetCellWidth = (iMode & ED_MODE_USE_CELL_WIDTH) ? TRUE : FALSE;
|
|
XP_Bool bClearCellWidth = (iMode & ED_MODE_NO_CELL_WIDTH) ? TRUE : FALSE;
|
|
XP_Bool bSetCellHeight = (iMode & ED_MODE_USE_CELL_HEIGHT) ? TRUE : FALSE;
|
|
XP_Bool bClearCellHeight = (iMode & ED_MODE_NO_CELL_HEIGHT) ? TRUE : FALSE;
|
|
|
|
//XP_TRACE(("SetSizeMode: bPixels=%d, bPercent=%d, bSetCellWidth=%d, bClearCellWidth=%d", bPixels, bPercent, bSetCellWidth, bClearCellWidth));
|
|
|
|
// Save current values to restore after resizing
|
|
m_bSaveWidthPercent = m_bWidthPercent;
|
|
m_bSaveHeightPercent = m_bHeightPercent;
|
|
|
|
// First set values for the table
|
|
if( (bTablePixels && m_bWidthPercent) || (bTablePercent && !m_bWidthPercent) ||
|
|
(bTablePixels && m_bHeightPercent) || (bTablePercent && !m_bHeightPercent) ||
|
|
bUseCols || bNoCols || bSetTableWidth || bSetTableHeight ||
|
|
bClearTableWidth || bClearTableHeight )
|
|
{
|
|
EDT_TableData *pTableData = GetData();
|
|
if( !pTableData )
|
|
return;
|
|
|
|
// Save these to restore after relayout of table
|
|
m_bSaveWidthDefined = pTableData->bWidthDefined;
|
|
m_bSaveHeightDefined = pTableData->bHeightDefined;
|
|
m_bSaveWidthPercent = pTableData->bWidthPercent;
|
|
m_bSaveHeightPercent = pTableData->bHeightPercent;
|
|
|
|
GetParentSize(pContext, &iParentWidth, &iParentHeight);
|
|
|
|
if( (pTableData->bWidthDefined && bClearTableWidth) ||
|
|
(!pTableData->bWidthDefined && bSetTableWidth) )
|
|
{
|
|
pTableData->bWidthDefined = bSetTableWidth;
|
|
}
|
|
if( (pTableData->bHeightDefined && bClearTableHeight) ||
|
|
(!pTableData->bHeightDefined && bSetTableHeight) )
|
|
{
|
|
pTableData->bHeightDefined = bSetTableHeight;
|
|
}
|
|
|
|
if( (bTablePixels && pTableData->bWidthPercent) ||
|
|
(bTablePercent && !pTableData->bWidthPercent) )
|
|
{
|
|
if( bTablePercent )
|
|
{
|
|
pTableData->bWidthPercent = TRUE;
|
|
pTableData->iWidth = m_iWidthPixels * 100 / iParentWidth;
|
|
} else if( bTablePixels ){
|
|
pTableData->bWidthPercent = FALSE;
|
|
pTableData->iWidth = m_iWidthPixels;
|
|
}
|
|
}
|
|
if( (bTablePixels && pTableData->bHeightPercent) ||
|
|
(bTablePercent && !pTableData->bHeightPercent) )
|
|
{
|
|
pTableData->bHeightPercent = bTablePercent;
|
|
if( bTablePercent )
|
|
{
|
|
pTableData->bHeightPercent = TRUE;
|
|
pTableData->iHeight = m_iHeightPixels * 100 / iParentHeight;
|
|
} else if( bTablePixels ){
|
|
pTableData->bHeightPercent = FALSE;
|
|
pTableData->iHeight = m_iHeightPixels;
|
|
}
|
|
}
|
|
|
|
if( bUseCols )
|
|
pTableData->bUseCols = TRUE;
|
|
if( bNoCols )
|
|
pTableData->bUseCols = FALSE;
|
|
|
|
SetData(pTableData);
|
|
EDT_FreeTableData(pTableData);
|
|
}
|
|
|
|
// Process cells only if asked to set or clear the width or height
|
|
if( bCellPixels || bCellPercent ||
|
|
bSetCellWidth || bClearCellWidth ||
|
|
bSetCellHeight || bClearCellHeight )
|
|
{
|
|
// Scan through entire table to set all cells
|
|
CEditTableCellElement *pCell = GetFirstCell();
|
|
if( pCell )
|
|
{
|
|
// Get current width and height
|
|
// (doesn't include amount we are changing
|
|
// during current resizing)
|
|
iParentWidth = pCell->GetParentWidth();
|
|
iParentHeight = pCell->GetParentHeight();
|
|
}
|
|
|
|
while( pCell )
|
|
{
|
|
// Only set cell's data if we change from current settings
|
|
bChanged = FALSE;
|
|
EDT_TableCellData *pCellData = pCell->GetData();
|
|
if( pCellData )
|
|
{
|
|
if( (pCellData->bWidthDefined && bClearCellWidth) ||
|
|
(!pCellData->bWidthDefined && bSetCellWidth) )
|
|
{
|
|
pCellData->bWidthDefined = bSetCellWidth;
|
|
bChanged = TRUE;
|
|
}
|
|
if( (pCellData->bHeightDefined && bClearCellHeight) ||
|
|
(!pCellData->bHeightDefined && bSetCellHeight) )
|
|
{
|
|
pCellData->bHeightDefined = bSetCellHeight;
|
|
bChanged = TRUE;
|
|
}
|
|
|
|
// Save the current m_bWidthPercent, m_bHeightPercent, bWidthDefined, and bHeightDefined
|
|
pCell->SaveSizeMode(pCellData->bWidthDefined, pCellData->bHeightDefined);
|
|
|
|
if( bSetTableWidth &&
|
|
(bCellPixels && pCellData->bWidthPercent ||
|
|
bCellPercent && !pCellData->bWidthPercent) )
|
|
{
|
|
if( bCellPercent )
|
|
{
|
|
pCellData->bWidthPercent = TRUE;
|
|
pCellData->iWidth = (pCellData->iWidthPixels * 100) / iParentWidth;
|
|
} else {
|
|
pCellData->bWidthPercent = FALSE;
|
|
pCellData->iWidth = pCellData->iWidthPixels;
|
|
}
|
|
bChanged = TRUE;
|
|
}
|
|
if( bSetTableHeight &&
|
|
(bCellPixels && pCellData->bHeightPercent ||
|
|
bCellPercent && !pCellData->bHeightPercent) )
|
|
{
|
|
if( bCellPercent )
|
|
{
|
|
pCellData->bHeightPercent = TRUE;
|
|
pCellData->iHeight = (pCellData->iHeightPixels * 100) / iParentHeight;
|
|
} else {
|
|
pCellData->bHeightPercent = FALSE;
|
|
pCellData->iHeight = pCellData->iHeightPixels;
|
|
}
|
|
bChanged = TRUE;
|
|
}
|
|
|
|
if( bChanged )
|
|
pCell->SetData(pCellData);
|
|
|
|
EDT_FreeTableCellData(pCellData);
|
|
}
|
|
pCell = GetNextCellInTable();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CEditTableElement::RestoreSizeMode(MWContext *pContext)
|
|
{
|
|
int32 iParentWidth;
|
|
int32 iParentHeight;
|
|
EDT_TableData *pTableData = GetData();
|
|
|
|
if( pTableData && pContext )
|
|
{
|
|
// First restore the size mode for the table
|
|
GetParentSize(pContext, &iParentWidth, &iParentHeight);
|
|
if( m_bSaveWidthPercent )
|
|
{
|
|
// Convert existing Pixel value to % of parent size
|
|
pTableData->iWidth = (m_iWidthPixels * 100) / iParentWidth;
|
|
pTableData->iHeight = (m_iHeightPixels * 100) / iParentHeight;
|
|
} else {
|
|
// Converting to pixels is simple - just use the pixel value we already have
|
|
pTableData->iWidth = m_iWidthPixels;
|
|
pTableData->iHeight = m_iHeightPixels;
|
|
}
|
|
pTableData->bWidthDefined = m_bSaveWidthDefined;
|
|
pTableData->bHeightDefined = m_bSaveHeightDefined;
|
|
pTableData->bWidthPercent = m_bSaveWidthPercent;
|
|
pTableData->bHeightPercent = m_bSaveHeightPercent;
|
|
SetData(pTableData);
|
|
EDT_FreeTableData(pTableData);
|
|
|
|
// Scan through entire table to reset all cells
|
|
CEditTableCellElement *pCell = GetFirstCell();
|
|
if( pCell )
|
|
{
|
|
iParentWidth = pCell->GetParentWidth();
|
|
iParentHeight = pCell->GetParentHeight();
|
|
}
|
|
while( pCell )
|
|
{
|
|
pCell->RestoreSizeMode(iParentWidth, iParentHeight);
|
|
pCell = GetNextCellInTable();
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// This should only be called before laying out a table and
|
|
// we expect to call AddLayoutData soon after to rebuild layout data and counters
|
|
void CEditTableElement::DeleteLayoutData()
|
|
{
|
|
// Blank out current arrays and reset row and column counters
|
|
memset(m_pColumnLayoutData, 0, m_iColumnLayoutSize*sizeof(EDT_CellLayoutData));
|
|
memset(m_pRowLayoutData, 0, m_iRowLayoutSize*sizeof(EDT_CellLayoutData));
|
|
m_iColumns = 0;
|
|
m_iRows = 0;
|
|
}
|
|
|
|
// Store data for first cell in each column and row in growable arrays
|
|
void CEditTableElement::AddLayoutData(CEditTableCellElement *pEdCell, LO_Element *pLoCell)
|
|
{
|
|
if( !pEdCell || !pLoCell )
|
|
return;
|
|
|
|
EDT_CellLayoutData *pColData;
|
|
EDT_CellLayoutData *pRowData;
|
|
|
|
intn i;
|
|
// We always add the cell the first time here
|
|
XP_Bool bNewColumn = m_iColumns == 0;
|
|
XP_Bool bNewRow = m_iRows == 0;
|
|
|
|
for( i = 0; i < m_iColumns; i++ )
|
|
{
|
|
if( pLoCell->lo_cell.x == m_pColumnLayoutData[i].X )
|
|
{
|
|
// We already have the first cell in this column
|
|
// Just increase the column count
|
|
// Note: We count geometric columns, so we must look at COLSPAN
|
|
m_pColumnLayoutData[i].iCellsInColumn += lo_GetColSpan(pLoCell);
|
|
break;
|
|
}
|
|
else if( pLoCell->lo_cell.x > m_pColumnLayoutData[i].X &&
|
|
( i+1 == m_iColumns ||
|
|
pLoCell->lo_cell.x < m_pColumnLayoutData[i+1].X ) )
|
|
{
|
|
// We are at the last existing cell in array or
|
|
// this cell's X is between current cell's and
|
|
// the next cell in array
|
|
// so we have a new column to insert between existing columns
|
|
bNewColumn = TRUE;
|
|
// The index to insert at is the NEXT location
|
|
i++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( bNewColumn )
|
|
{
|
|
if( m_iColumns >= m_iColumnLayoutSize )
|
|
{
|
|
m_pColumnLayoutData = (EDT_CellLayoutData*)XP_REALLOC(m_pColumnLayoutData, (LAYOUT_ARRAY_SIZE+m_iColumnLayoutSize)*sizeof(EDT_CellLayoutData));
|
|
if( m_pColumnLayoutData )
|
|
{
|
|
m_iColumnLayoutSize += LAYOUT_ARRAY_SIZE;
|
|
}
|
|
else
|
|
{
|
|
m_iColumnLayoutSize = 0;
|
|
XP_TRACE(("Out of memory for ColumnLayout data"));
|
|
XP_ASSERT(FALSE);
|
|
return;
|
|
}
|
|
}
|
|
// Make room for inserted struct if not at end of current array
|
|
if( i < m_iColumns )
|
|
XP_MEMMOVE(&m_pColumnLayoutData[i+1], &m_pColumnLayoutData[i], (m_iColumns-i)*sizeof(EDT_CellLayoutData));
|
|
|
|
pColData = &m_pColumnLayoutData[i];
|
|
pColData->X = pLoCell->lo_cell.x;
|
|
pColData->Y = pLoCell->lo_cell.y;
|
|
pColData->pEdCell = pEdCell;
|
|
pColData->pLoCell = pLoCell;
|
|
pColData->iColumn = i;
|
|
pColData->iCellsInColumn = lo_GetColSpan(pLoCell);
|
|
// These are ignored for column data
|
|
pColData->iCellsInRow = pColData->iRow = 0;
|
|
|
|
m_iColumns++;
|
|
}
|
|
|
|
for( i = 0; i < m_iRows; i++ )
|
|
{
|
|
if( pLoCell->lo_cell.y == m_pRowLayoutData[i].Y )
|
|
{
|
|
// We already have the first cell in this row
|
|
// Just increase the row count
|
|
m_pRowLayoutData[i].iCellsInRow += lo_GetRowSpan(pLoCell);
|
|
break;
|
|
}
|
|
else if( pLoCell->lo_cell.y > m_pRowLayoutData[i].Y &&
|
|
( i+1 == m_iRows ||
|
|
pLoCell->lo_cell.y < m_pRowLayoutData[i+1].Y ) )
|
|
{
|
|
// We are at the last existing cell in array or
|
|
// this cell's Y is between current cell's and
|
|
// the next cell in array,
|
|
// so we have a new row to insert between existing rows
|
|
bNewRow = TRUE;
|
|
// The index to insert at is the NEXT location
|
|
i++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( bNewRow )
|
|
{
|
|
if( m_iRows >= m_iRowLayoutSize )
|
|
{
|
|
m_pRowLayoutData = (EDT_CellLayoutData*)XP_REALLOC(m_pRowLayoutData, (LAYOUT_ARRAY_SIZE+m_iRowLayoutSize)*sizeof(EDT_CellLayoutData));
|
|
if( m_pRowLayoutData )
|
|
{
|
|
m_iRowLayoutSize += LAYOUT_ARRAY_SIZE;
|
|
}
|
|
else
|
|
{
|
|
m_iRowLayoutSize = 0;
|
|
XP_TRACE(("Out of memory for RowLayout data"));
|
|
XP_ASSERT(FALSE);
|
|
return;
|
|
}
|
|
}
|
|
// Make room for inserted struct if not at end of current array
|
|
if( i < m_iRows )
|
|
XP_MEMMOVE(&m_pRowLayoutData[i+1], &m_pRowLayoutData[i], (m_iRows-i)*sizeof(EDT_CellLayoutData));
|
|
|
|
pRowData = &m_pRowLayoutData[i];
|
|
|
|
pRowData->X = pLoCell->lo_cell.x;
|
|
pRowData->Y = pLoCell->lo_cell.y;
|
|
pRowData->pEdCell = pEdCell;
|
|
pRowData->pLoCell = pLoCell;
|
|
pRowData->iRow = i;
|
|
pRowData->iCellsInRow = lo_GetRowSpan(pLoCell);
|
|
// These are ignored for row data
|
|
pRowData->iCellsInColumn = pRowData->iColumn = 0;
|
|
|
|
m_iRows++;
|
|
//m_pRowLayoutData.Insert(pRowData, i);
|
|
}
|
|
}
|
|
|
|
// class CEditTableRowElement
|
|
|
|
CEditTableRowElement::CEditTableRowElement()
|
|
: CEditElement((CEditElement*) NULL, P_TABLE_ROW),
|
|
m_backgroundColor(),
|
|
m_iBackgroundSaveIndex(0),
|
|
m_pBackgroundImage(0)
|
|
{
|
|
}
|
|
|
|
CEditTableRowElement::CEditTableRowElement(intn columns)
|
|
: CEditElement((CEditElement*) NULL, P_TABLE_ROW),
|
|
m_backgroundColor(),
|
|
m_iBackgroundSaveIndex(0),
|
|
m_pBackgroundImage(0)
|
|
{
|
|
EDT_TRY {
|
|
for ( intn column = 0; column < columns; column++ ) {
|
|
CEditTableCellElement* pCell = new CEditTableCellElement();
|
|
pCell->InsertAsFirstChild(this);
|
|
}
|
|
}
|
|
EDT_CATCH_ALL {
|
|
Finalize();
|
|
EDT_RETHROW;
|
|
}
|
|
}
|
|
|
|
CEditTableRowElement::CEditTableRowElement(CEditElement *pParent, PA_Tag *pTag, int16 /*csid*/)
|
|
: CEditElement(pParent, P_TABLE_ROW),
|
|
m_backgroundColor(),
|
|
m_iBackgroundSaveIndex(0),
|
|
m_pBackgroundImage(0)
|
|
{
|
|
if( pTag ){
|
|
char *locked_buff;
|
|
|
|
PA_LOCK(locked_buff, char *, pTag->data );
|
|
if( locked_buff ){
|
|
SetTagData( locked_buff );
|
|
}
|
|
PA_UNLOCK(pTag->data);
|
|
}
|
|
}
|
|
|
|
CEditTableRowElement::CEditTableRowElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer)
|
|
: CEditElement(pStreamIn, pBuffer),
|
|
m_backgroundColor(),
|
|
m_iBackgroundSaveIndex(0),
|
|
m_pBackgroundImage(0)
|
|
{
|
|
}
|
|
|
|
CEditTableRowElement::~CEditTableRowElement(){
|
|
delete m_pBackgroundImage;
|
|
}
|
|
|
|
CEditTableRowElement *CEditTableRowElement::GetNextRow()
|
|
{
|
|
CEditElement *pRow = GetNextSibling();
|
|
if( pRow && pRow->IsTableRow() )
|
|
{
|
|
return pRow->TableRow();
|
|
}
|
|
else if( pRow )
|
|
{
|
|
// Most likely we have a caption
|
|
XP_ASSERT(pRow->IsCaption());
|
|
// Can only have 1, so next one must
|
|
// be a row or we're at the end already
|
|
pRow = pRow->GetNextSibling();
|
|
if( pRow && pRow->IsTableRow() )
|
|
return pRow->TableRow();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
CEditTableCellElement *CEditTableRowElement::GetFirstCell()
|
|
{
|
|
CEditElement *pChild = GetChild();
|
|
if( pChild && pChild->IsTableCell() )
|
|
{
|
|
return pChild->TableCell();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// Scan all cells in row and add up m_iColSpan to get
|
|
// maximum possible cells in the row
|
|
intn CEditTableRowElement::GetCells()
|
|
{
|
|
intn cells = 0;
|
|
// Get the first cell of the row
|
|
CEditElement* pChild = GetChild();
|
|
|
|
if( pChild && pChild->IsTableCell() )
|
|
{
|
|
// Starting number of cells is normally 0,
|
|
// except when ROWSPAN causes first cell to be indented
|
|
while( pChild )
|
|
{
|
|
if ( pChild->IsTableCell() )
|
|
{
|
|
// We must add column span to get maximum possible columns per row
|
|
cells += ((CEditTableCellElement*)pChild)->GetColSpan();
|
|
}
|
|
pChild = pChild->GetNextSibling();
|
|
}
|
|
}
|
|
return cells;
|
|
}
|
|
|
|
XP_Bool CEditTableRowElement::AllCellsInRowAreEmpty()
|
|
{
|
|
CEditTableCellElement *pCell = GetFirstCell();
|
|
if( !pCell || !pCell->IsTableCell() ) // Unlikely
|
|
{
|
|
XP_ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
while( pCell && pCell->IsTableCell() )
|
|
{
|
|
if( !pCell->IsEmpty() )
|
|
return FALSE;
|
|
|
|
pCell = (CEditTableCellElement*)(pCell->GetNextSibling());
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void CEditTableRowElement::FinishedLoad( CEditBuffer* pBuffer ){
|
|
// Wrap anything that's not a table cell in a table cell.
|
|
CEditTableCellElement* pCell = NULL;
|
|
CEditElement* pNext = 0;
|
|
|
|
if ( ! GetChild() ) {
|
|
// Force a cell
|
|
CEditTableCellElement* pTempCell = new CEditTableCellElement();
|
|
pTempCell->InsertAsFirstChild(this);
|
|
}
|
|
|
|
for ( CEditElement* pChild = GetChild();
|
|
pChild;
|
|
pChild = pNext) {
|
|
pNext = pChild->GetNextSibling(); // We might unlink pChild
|
|
if ( ! IsAcceptableChild(*pChild) ){
|
|
if ( ! pCell ){
|
|
pCell = new CEditTableCellElement();
|
|
pCell->InsertAfter(pChild);
|
|
}
|
|
pChild->Unlink();
|
|
pChild->InsertAsLastChild(pCell);
|
|
}
|
|
else {
|
|
if ( pCell ){
|
|
pCell->FinishedLoad(pBuffer);
|
|
pCell = NULL;
|
|
}
|
|
}
|
|
pChild->FinishedLoad(pBuffer);
|
|
}
|
|
if ( pCell ){
|
|
pCell->FinishedLoad(pBuffer);
|
|
pCell = NULL;
|
|
}
|
|
}
|
|
|
|
static void edt_InsertCells(intn number, XP_Bool bAfter, CEditTableCellElement *pExisting,
|
|
CEditTableRowElement *pSourceRow, CEditTableCellElement **ppCellForInsertPoint)
|
|
{
|
|
XP_ASSERT(pExisting);
|
|
if( !pExisting )
|
|
return;
|
|
CEditBuffer *pBuffer = pExisting->GetEditBuffer();
|
|
if( !pBuffer )
|
|
return;
|
|
|
|
// Insert new cells at new insert position
|
|
for ( intn col = 0; col < number; col++ )
|
|
{
|
|
CEditTableCellElement *pInsertCell = NULL;
|
|
if ( pSourceRow )
|
|
{
|
|
// Remove cells from source from left to right
|
|
pInsertCell = pSourceRow->GetFirstCell();
|
|
if( pInsertCell )
|
|
{
|
|
// We found a cell - unlink it from source row
|
|
// Note: When pasting a column, we get a NULL cell
|
|
// if any cell in the column has COLSPAN > 1.
|
|
// The cells without colspan will be "missing" in the source
|
|
pInsertCell->Unlink();
|
|
|
|
// If inserted cell spans > 1 column, then
|
|
// we should insert fewer to keep table rectangular
|
|
intn iColSpan = pInsertCell->GetColSpan();
|
|
if( iColSpan > 1 )
|
|
number -= iColSpan - 1;
|
|
}
|
|
}
|
|
// We didn't find a cell in source
|
|
// (no source or because of COLSPAN > 1),
|
|
// so insert a blank cell instead
|
|
if( !pInsertCell )
|
|
pInsertCell = new CEditTableCellElement();
|
|
|
|
if( pInsertCell )
|
|
{
|
|
if( bAfter )
|
|
{
|
|
pInsertCell->InsertAfter(pExisting);
|
|
// To insert new cells when pSourceRow is used
|
|
// and we want to insert from left to right,
|
|
// set the "before" cell to the one just inserted
|
|
pExisting = pInsertCell;
|
|
}
|
|
else
|
|
{
|
|
// Insert before the current column
|
|
// This will add pSourceRow cells from left to right
|
|
pInsertCell->InsertBefore(pExisting);
|
|
}
|
|
// Be sure cell has the empty text element in default paragraph container,
|
|
// but check if text should be a space
|
|
pBuffer->SetFillNewCellWithSpace();
|
|
pInsertCell->FinishedLoad(pBuffer);
|
|
pBuffer->ClearFillNewCellWithSpace();
|
|
}
|
|
// Return the cell inserted if not already set
|
|
if( ppCellForInsertPoint && *ppCellForInsertPoint == NULL )
|
|
*ppCellForInsertPoint = pInsertCell;
|
|
}
|
|
}
|
|
|
|
// Note: iNewX should either be == X (insert before current column)
|
|
// or == X + (insert column's width).
|
|
void CEditTableRowElement::InsertCells(int32 X, int32 iNewX, intn number, CEditTableRowElement* pSourceRow,
|
|
CEditTableCellElement **ppCellForInsertPoint)
|
|
{
|
|
if ( number <= 0 || X < 0 )
|
|
{
|
|
return;
|
|
}
|
|
CEditTableElement *pTable = (CEditTableElement*)GetParent();
|
|
if( !pTable || !pTable->IsTable() )
|
|
return;
|
|
|
|
CEditTableCellElement *pCellForInsertPoint = NULL;
|
|
|
|
int32 iInterCellSpace = pTable->GetCellSpacing();
|
|
int32 iCellX;
|
|
if ( pTable->GetColumnX(0) == iNewX )
|
|
{
|
|
// Insert at the start.
|
|
for ( intn col = 0; col < number; col++ )
|
|
{
|
|
CEditTableCellElement* pInsertCell = NULL;
|
|
if ( pSourceRow )
|
|
{
|
|
// Insert cells from the end
|
|
pInsertCell = (CEditTableCellElement*)pSourceRow->GetLastChild();
|
|
if( pInsertCell )
|
|
{
|
|
pInsertCell->Unlink();
|
|
|
|
// If inserted cell spans > 1 column, then
|
|
// we should insert fewer to keep table rectangular
|
|
intn iColSpan = pInsertCell->GetColSpan();
|
|
if( iColSpan > 1 )
|
|
number -= iColSpan - 1;
|
|
}
|
|
}
|
|
//Note: Create a new cell if no source or source row was incomplete
|
|
if( !pInsertCell )
|
|
{
|
|
pInsertCell = new CEditTableCellElement();
|
|
// Insert space into initial text
|
|
CEditBuffer *pBuffer = GetEditBuffer();
|
|
pBuffer->SetFillNewCellWithSpace();
|
|
pInsertCell->FinishedLoad(pBuffer);
|
|
pBuffer->ClearFillNewCellWithSpace();
|
|
}
|
|
if( pInsertCell )
|
|
{
|
|
// Save first cell inserted
|
|
if( !pCellForInsertPoint )
|
|
pCellForInsertPoint = pInsertCell;
|
|
|
|
pInsertCell->InsertAsFirstChild(this);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Get the cell at the requested column
|
|
CEditTableCellElement* pExisting = FindCell(X, X != iNewX);
|
|
// This may now be NULL for "missing" cells because
|
|
// of ROWSPAN or COLSPAN effects
|
|
if( pExisting )
|
|
{
|
|
iCellX = pExisting->GetX();
|
|
if( (iCellX < X || X != iNewX) && ((iCellX + pExisting->GetFullWidth()) > iNewX) ) // WAS GetWidth()
|
|
{
|
|
// Cell extends past new column insert point,
|
|
// so simply increase span and don't add new cell
|
|
pExisting->IncreaseColSpan(number);
|
|
} else {
|
|
// Non-spanned cell found at current column,
|
|
// so insert before (X==iNewX) or after that cell
|
|
edt_InsertCells(number, X < iNewX, pExisting, pSourceRow, &pCellForInsertPoint);
|
|
}
|
|
}
|
|
else if( (X != iNewX) && (pExisting = FindCell(iNewX/*+iInterCellSpace*/)) != NULL )
|
|
{
|
|
// No cell in current column, but we found cell at NEW insert column
|
|
// so put new cell(s) before this
|
|
edt_InsertCells(number, FALSE, pExisting, pSourceRow, &pCellForInsertPoint);
|
|
}
|
|
else
|
|
{
|
|
// We didn't find a cell, either because of cells spanning row
|
|
// from above, or this row needs new cell(s) at the end
|
|
|
|
// First cell in current row
|
|
CEditTableCellElement *pFirstCell = GetFirstCell();
|
|
if( !pFirstCell )
|
|
goto INSERT_CELLS_DONE;
|
|
|
|
// This should be same as pFirstCell, but we need to setup
|
|
// to find other cells in same geometric row
|
|
CEditTableCellElement *pCell = pTable->GetFirstCellInRow(pFirstCell->GetY());
|
|
XP_ASSERT(pCell == pFirstCell);
|
|
intn iCurrentRow = pFirstCell->GetRow();
|
|
|
|
CEditTableCellElement *pCellBefore = NULL;
|
|
CEditTableCellElement *pCellAfter = NULL;
|
|
|
|
while(pCell)
|
|
{
|
|
iCellX = pCell->GetX();
|
|
int32 iCellWidth = pCell->GetFullWidth(); // WAS GetWidth()
|
|
if( iCellX == X )
|
|
{
|
|
// We found a cell from another row that spans current row
|
|
// and target column, so just increase its column span instead of inserting
|
|
if( X != iNewX && (iCellX+iCellWidth) > iNewX )
|
|
{
|
|
pCell->IncreaseColSpan(number);
|
|
goto INSERT_CELLS_DONE;
|
|
}
|
|
}
|
|
// Find the closest cells in current row
|
|
// to our target column
|
|
if( pCell->GetRow() == iCurrentRow )
|
|
{
|
|
if( (iCellX + pCell->GetFullWidth()) <= X )
|
|
pCellBefore = pCell;
|
|
if( iCellX >= X && !pCellAfter )
|
|
pCellAfter = pCell;
|
|
}
|
|
pCell = pTable->GetNextCellInRow();
|
|
}
|
|
if( pCellAfter )
|
|
{
|
|
// FALSE = Force inserting before the cell found
|
|
edt_InsertCells(number, FALSE, pCellAfter, pSourceRow, &pCellForInsertPoint);
|
|
goto INSERT_CELLS_DONE;
|
|
}
|
|
if( pCellBefore )
|
|
{
|
|
// This is tough. We know we need to add cells, but how many?
|
|
// We can't be sure we had enough before so new cells end up
|
|
// at desired new column.
|
|
// TODO: USE pTable->m_pColumnLayoutData to figure out how
|
|
// many cells to add to bring us to desired X
|
|
// TRUE = insert after the cell found
|
|
edt_InsertCells(number, TRUE, pCellBefore, pSourceRow, &pCellForInsertPoint);
|
|
}
|
|
}
|
|
}
|
|
INSERT_CELLS_DONE:
|
|
// Return the cell inserted if not already set
|
|
if( ppCellForInsertPoint && *ppCellForInsertPoint == NULL )
|
|
*ppCellForInsertPoint = pCellForInsertPoint;
|
|
}
|
|
|
|
intn CEditTableRowElement::AppendRow( CEditTableRowElement *pAppendRow, XP_Bool bDeleteRow )
|
|
{
|
|
if( !pAppendRow )
|
|
return 0;
|
|
|
|
intn iColumnsAppended = 0;
|
|
CEditTableCellElement *pAppendCell = pAppendRow->GetFirstCell();
|
|
|
|
while( pAppendCell )
|
|
{
|
|
// Unlink each cell in source row and
|
|
// append it to current row
|
|
pAppendCell->Unlink();
|
|
pAppendCell->InsertAsLastChild(this);
|
|
iColumnsAppended += pAppendCell->GetColSpan();
|
|
pAppendCell = pAppendRow->GetFirstCell();
|
|
}
|
|
|
|
if( bDeleteRow )
|
|
delete pAppendRow;
|
|
|
|
return iColumnsAppended;
|
|
}
|
|
|
|
// This only works for table rows already layed out
|
|
// (has gone through CEditBuffer::FixupTableData() )
|
|
void CEditTableRowElement::PadRowWithEmptyCells( intn iColumns )
|
|
{
|
|
CEditTableElement *pTable = (CEditTableElement*)GetParent();
|
|
if( !pTable || !pTable->IsTable() )
|
|
return;
|
|
CEditTableCellElement *pCell = GetFirstCell();
|
|
CEditTableCellElement *pLastCell = NULL;
|
|
|
|
// Use number of columns supplied, or get current number in table
|
|
intn iTotalColumns = iColumns ? iColumns : pTable->GetColumns();
|
|
|
|
while( pCell )
|
|
{
|
|
pLastCell = pCell;
|
|
pCell = pCell->GetNextCellInLogicalRow();
|
|
}
|
|
if( pLastCell )
|
|
{
|
|
// Get which column the last cell is in
|
|
intn iLastIndex = pTable->GetColumnIndex(pLastCell->GetX());
|
|
if( iLastIndex >= 0 )
|
|
{
|
|
intn iExtraColumns = iTotalColumns - iLastIndex - 1;
|
|
if( iExtraColumns > 0 )
|
|
edt_InsertCells( iExtraColumns, TRUE, pLastCell, NULL, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CEditTableRowElement::DeleteCells(int32 X, intn number, CEditTableCellElement **ppCellForInsertPoint)
|
|
{
|
|
if ( number == 0 )
|
|
{
|
|
return; /* A waste of time, but legal. */
|
|
}
|
|
|
|
if ( number < 0 || X < 0 )
|
|
{
|
|
/* Illegal. */
|
|
XP_ASSERT(FALSE);
|
|
return;
|
|
}
|
|
CEditTableElement *pTable = (CEditTableElement*)GetParent();
|
|
XP_ASSERT(pTable && pTable->IsTable() );
|
|
|
|
CEditTableCellElement *pCellForInsertPoint = NULL;
|
|
|
|
CEditTableCellElement* pCell = FindCell(X);
|
|
CEditTableCellElement* pPrevCell;
|
|
|
|
if( pTable && pCell )
|
|
{
|
|
// Save the first cell that we will NOT be deleting
|
|
if( X == pCell->GetX() )
|
|
{
|
|
pPrevCell = (CEditTableCellElement*)pCell->GetPreviousSibling();
|
|
#ifdef DEBUG
|
|
if( pPrevCell )
|
|
XP_ASSERT(pPrevCell->IsTableCell());
|
|
#endif
|
|
}
|
|
else
|
|
// Cell spans delete X, so we will only be reducing cellspan
|
|
pPrevCell = pCell;
|
|
|
|
intn iRemaining = number;
|
|
|
|
CEditTableCellElement *pNextCell = NULL;
|
|
while( iRemaining > 0 )
|
|
{
|
|
// Get next cell before we delete the one we're at
|
|
pNextCell = pCell->GetNextCellInLogicalRow();
|
|
|
|
int32 iCellX = pCell->GetX();
|
|
int32 iColSpan = pCell->GetColSpan();
|
|
|
|
if( iCellX >= X )
|
|
{
|
|
// Cell is in same column as delete column or to the right of it
|
|
if( iColSpan <= iRemaining )
|
|
{
|
|
// Simplest case - just delete one cell
|
|
|
|
// Remove any selected cells from our lists
|
|
// else relayout blows up
|
|
// NULL means let function find the corresponding LO_CellStruct
|
|
if( pCell->IsSelected() )
|
|
GetEditBuffer()->SelectCell(FALSE, NULL, pCell);
|
|
|
|
delete pCell;
|
|
iRemaining -= iColSpan;
|
|
}
|
|
else if( iColSpan > iRemaining )
|
|
{
|
|
// Cell spans more than we want to delete,
|
|
// so reduce span instead of actually deleting
|
|
pCell->DecreaseColSpan(iRemaining);
|
|
iRemaining = 0;
|
|
// Since this cell is in the delete column (same X),
|
|
// then we want it to look deleted, so empty contents
|
|
pCell->DeleteContents();
|
|
}
|
|
} else {
|
|
// Start of cell is before delete column
|
|
// (but end must be past delete column)
|
|
// Figure out how many columns it spans before delete column.
|
|
int32 iSpanAvailable = iColSpan - pTable->GetColumnsSpanned(iCellX, X);
|
|
if( iSpanAvailable > 0 )
|
|
{
|
|
// Decrease the column span instead of deleting
|
|
intn iDecrease = min(iSpanAvailable,iRemaining);
|
|
pCell->DecreaseColSpan(iDecrease);
|
|
iRemaining -= iDecrease;
|
|
// "Undefine" (1st param) the width since it is no longer
|
|
// in the same column as before
|
|
// Keep existing bWidthPercent, the last param = width
|
|
// (it doesn't matter what it is now)
|
|
pCell->SetWidth(FALSE, pCell->GetWidthPercent(), 1);
|
|
// then we want to use the width of the
|
|
}
|
|
}
|
|
// We are done if no more cells in row or nothing remaining to delete
|
|
if( !pNextCell )
|
|
break;
|
|
|
|
pCell = pNextCell;
|
|
}
|
|
// Cell for insert point is the first non-deleted cell
|
|
// (which should end up where the first deleted cell was)
|
|
// or the previous cell if we deleted to the end of row
|
|
pCellForInsertPoint = pNextCell ? pNextCell : pPrevCell;
|
|
} else {
|
|
//TODO: WHAT TO DO IF WE DON'T FIND A CELL?
|
|
// Only case this should fail is if number of cells in this row
|
|
// is less than the column being deleted
|
|
// So doing nothing is probably OK
|
|
XP_TRACE(("No cell in column at X = %d", X));
|
|
}
|
|
// Return the cell inserted if not already set
|
|
if( ppCellForInsertPoint && *ppCellForInsertPoint == NULL )
|
|
*ppCellForInsertPoint = pCellForInsertPoint;
|
|
}
|
|
|
|
CEditTableCellElement* CEditTableRowElement::FindCell(int32 X, XP_Bool bIncludeRightEdge)
|
|
{
|
|
CEditTableCellElement* pCell = NULL;
|
|
XP_Bool bQuitNextCycle = FALSE;
|
|
|
|
for ( CEditElement* pChild = GetChild();
|
|
pChild;
|
|
pChild = pChild->GetNextSibling())
|
|
{
|
|
if ( pChild->IsTableCell() )
|
|
{
|
|
int32 cellX = pChild->TableCell()->GetX();
|
|
int32 cellRight = cellX + pChild->TableCell()->GetFullWidth(); // WAS GetWidth()
|
|
|
|
// Back up 1 pixel if we shouldn't match the right edge
|
|
if( !bIncludeRightEdge )
|
|
cellRight--;
|
|
|
|
if ( X >= cellX && X <= cellRight )
|
|
{
|
|
if( pCell )
|
|
{
|
|
return pChild->TableCell();
|
|
} else {
|
|
// We are here when we first find a cell
|
|
// Save it but continue 1 more cycle to see if next cell's
|
|
// left edge matches - use that instead if it does
|
|
// (This is needed when border = 0, then the
|
|
// right edge of one cell = left edge of next)
|
|
pCell = pChild->TableCell();
|
|
bQuitNextCycle = TRUE;
|
|
continue; // Skip test just below
|
|
}
|
|
}
|
|
if( bQuitNextCycle )
|
|
break;
|
|
}
|
|
}
|
|
return pCell;
|
|
}
|
|
|
|
void CEditTableRowElement::SetData( EDT_TableRowData *pData ){
|
|
char *pNew = 0;
|
|
if( pData->align != ED_ALIGN_DEFAULT ){
|
|
pNew = PR_sprintf_append( pNew, "ALIGN=%s ", EDT_AlignString(pData->align) );
|
|
}
|
|
|
|
if( pData->valign != ED_ALIGN_DEFAULT ){
|
|
pNew = PR_sprintf_append( pNew, "VALIGN=%s ", EDT_AlignString(pData->valign) );
|
|
}
|
|
|
|
if ( pData->pColorBackground ) {
|
|
SetBackgroundColor(EDT_LO_COLOR(pData->pColorBackground));
|
|
pNew = PR_sprintf_append( pNew, "BGCOLOR=\"#%06lX\" ", GetBackgroundColor().GetAsLong() );
|
|
}
|
|
else {
|
|
SetBackgroundColor(ED_Color::GetUndefined());
|
|
}
|
|
|
|
if ( pData->pBackgroundImage ) {
|
|
SetBackgroundImage(pData->pBackgroundImage);
|
|
pNew = PR_sprintf_append( pNew, "BACKGROUND=\"%s\" ", pData->pBackgroundImage );
|
|
}
|
|
else {
|
|
SetBackgroundImage(0);
|
|
}
|
|
if ( pData->bBackgroundNoSave) {
|
|
pNew = PR_sprintf_append( pNew, "NOSAVE " );
|
|
}
|
|
|
|
if( pData->pExtra ){
|
|
pNew = PR_sprintf_append( pNew, "%s ", pData->pExtra );
|
|
}
|
|
|
|
if( pNew ){
|
|
pNew = PR_sprintf_append( pNew, ">" );
|
|
}
|
|
SetTagData( pNew );
|
|
if ( pNew ) {
|
|
free(pNew);
|
|
}
|
|
}
|
|
|
|
EDT_TableRowData* CEditTableRowElement::GetData( ){
|
|
EDT_TableRowData *pRet;
|
|
PA_Tag* pTag = TagOpen(0);
|
|
pRet = ParseParams( pTag, GetWinCSID() );
|
|
PA_FreeTag( pTag );
|
|
return pRet;
|
|
}
|
|
|
|
static char *tableRowParams[] = {
|
|
PARAM_ALIGN,
|
|
PARAM_VALIGN,
|
|
PARAM_BGCOLOR,
|
|
PARAM_BACKGROUND,
|
|
PARAM_NOSAVE,
|
|
0
|
|
};
|
|
|
|
EDT_TableRowData* CEditTableRowElement::ParseParams( PA_Tag *pTag, int16 csid ){
|
|
EDT_TableRowData* pData = NewData();
|
|
ED_Alignment align;
|
|
|
|
align = edt_FetchParamAlignment( pTag, ED_ALIGN_DEFAULT, FALSE, csid );
|
|
if( align == ED_ALIGN_RIGHT || align == ED_ALIGN_LEFT || align == ED_ALIGN_ABSCENTER ){
|
|
pData->align = align;
|
|
}
|
|
align = edt_FetchParamAlignment( pTag, ED_ALIGN_DEFAULT, TRUE, csid );
|
|
if( align == ED_ALIGN_ABSTOP || align == ED_ALIGN_ABSBOTTOM || align == ED_ALIGN_ABSCENTER
|
|
|| align == ED_ALIGN_BASELINE ){
|
|
pData->valign = align;
|
|
}
|
|
pData->pColorBackground = edt_MakeLoColor(edt_FetchParamColor( pTag, PARAM_BGCOLOR, csid ));
|
|
pData->pBackgroundImage = edt_FetchParamString( pTag, PARAM_BACKGROUND, csid );
|
|
pData->bBackgroundNoSave = edt_FetchParamBoolExist( pTag, PARAM_NOSAVE, csid );
|
|
pData->pExtra = edt_FetchParamExtras( pTag, tableRowParams, csid );
|
|
|
|
return pData;
|
|
}
|
|
|
|
EDT_TableRowData* CEditTableRowElement::NewData(){
|
|
EDT_TableRowData* pData = XP_NEW( EDT_TableRowData );
|
|
if( pData == 0 ){
|
|
// throw();
|
|
return pData;
|
|
}
|
|
pData->align = ED_ALIGN_DEFAULT;
|
|
pData->valign = ED_ALIGN_TOP; // ED_ALIGN_DEFAULT; this is centered - stupid!
|
|
pData->pColorBackground = 0;
|
|
pData->pBackgroundImage = 0;
|
|
pData->bBackgroundNoSave = FALSE;
|
|
pData->pExtra = 0;
|
|
return pData;
|
|
}
|
|
|
|
void CEditTableRowElement::FreeData( EDT_TableRowData *pData ){
|
|
if( pData->pColorBackground ) XP_FREE( pData->pColorBackground );
|
|
if( pData->pBackgroundImage ) XP_FREE( pData->pBackgroundImage );
|
|
if( pData->pExtra ) XP_FREE( pData->pExtra );
|
|
XP_FREE( pData );
|
|
}
|
|
|
|
void CEditTableRowElement::SetBackgroundColor( ED_Color iColor ){
|
|
m_backgroundColor = iColor;
|
|
}
|
|
|
|
ED_Color CEditTableRowElement::GetBackgroundColor(){
|
|
return m_backgroundColor;
|
|
}
|
|
|
|
void CEditTableRowElement::SetBackgroundImage( char* pImage ){
|
|
delete m_pBackgroundImage;
|
|
m_pBackgroundImage = 0;
|
|
if ( pImage ) {
|
|
m_pBackgroundImage = XP_STRDUP(pImage);
|
|
}
|
|
}
|
|
|
|
char* CEditTableRowElement::GetBackgroundImage(){
|
|
return m_pBackgroundImage;
|
|
}
|
|
|
|
// class CEditCaptionElement
|
|
|
|
CEditCaptionElement::CEditCaptionElement()
|
|
: CEditSubDocElement((CEditElement*) NULL, P_CAPTION)
|
|
{
|
|
}
|
|
|
|
CEditCaptionElement::CEditCaptionElement(CEditElement *pParent, PA_Tag *pTag, int16 /*csid*/)
|
|
: CEditSubDocElement(pParent, P_CAPTION)
|
|
{
|
|
if( pTag ){
|
|
char *locked_buff;
|
|
|
|
PA_LOCK(locked_buff, char *, pTag->data );
|
|
if( locked_buff ){
|
|
SetTagData( locked_buff );
|
|
}
|
|
PA_UNLOCK(pTag->data);
|
|
}
|
|
}
|
|
|
|
CEditCaptionElement::CEditCaptionElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer)
|
|
: CEditSubDocElement(pStreamIn, pBuffer)
|
|
{
|
|
}
|
|
|
|
void CEditCaptionElement::SetData( EDT_TableCaptionData *pData ){
|
|
char *pNew = 0;
|
|
if( pData->align == ED_ALIGN_ABSBOTTOM ){
|
|
pNew = PR_sprintf_append( pNew, "ALIGN=%s ", EDT_AlignString(pData->align) );
|
|
}
|
|
if( pData->pExtra ){
|
|
pNew = PR_sprintf_append( pNew, "%s ", pData->pExtra );
|
|
}
|
|
|
|
if( pNew ){
|
|
pNew = PR_sprintf_append( pNew, ">" );
|
|
}
|
|
SetTagData( pNew );
|
|
if ( pNew ) {
|
|
free(pNew);
|
|
}
|
|
}
|
|
|
|
EDT_TableCaptionData* CEditCaptionElement::GetData( ){
|
|
return GetData( GetWinCSID() );
|
|
}
|
|
|
|
EDT_TableCaptionData* CEditCaptionElement::GetData( int16 csid ){
|
|
EDT_TableCaptionData* pRet;
|
|
PA_Tag* pTag = TagOpen(0);
|
|
pRet = ParseParams( pTag, csid );
|
|
PA_FreeTag( pTag );
|
|
return pRet;
|
|
}
|
|
|
|
static char *tableCaptionParams[] = {
|
|
PARAM_ALIGN,
|
|
0
|
|
};
|
|
|
|
EDT_TableCaptionData* CEditCaptionElement::ParseParams( PA_Tag *pTag, int16 csid ){
|
|
EDT_TableCaptionData* pData = NewData();
|
|
pData->align = edt_FetchParamAlignment( pTag, ED_ALIGN_ABSTOP, FALSE, csid );
|
|
pData->pExtra = edt_FetchParamExtras( pTag, tableCaptionParams, csid );
|
|
return pData;
|
|
}
|
|
|
|
EDT_TableCaptionData* CEditCaptionElement::NewData(){
|
|
EDT_TableCaptionData* pData = XP_NEW( EDT_TableCaptionData );
|
|
if( pData == 0 ){
|
|
// throw();
|
|
return pData;
|
|
}
|
|
pData->align = ED_ALIGN_CENTER;
|
|
pData->pExtra = 0;
|
|
return pData;
|
|
}
|
|
|
|
void CEditCaptionElement::FreeData( EDT_TableCaptionData *pData ){
|
|
if( pData->pExtra ) XP_FREE( pData->pExtra );
|
|
XP_FREE( pData );
|
|
}
|
|
|
|
// class CEditTableCellElement
|
|
|
|
CEditTableCellElement::CEditTableCellElement()
|
|
: CEditSubDocElement((CEditElement*) NULL, P_TABLE_DATA),
|
|
m_backgroundColor(),
|
|
m_iBackgroundSaveIndex(0),
|
|
m_pBackgroundImage(0),
|
|
m_iWidth(1),
|
|
m_iWidthPixels(1),
|
|
m_iHeight(1),
|
|
m_iHeightPixels(1),
|
|
m_bWidthPercent(FALSE),
|
|
m_bHeightPercent(FALSE),
|
|
m_bWidthDefined(FALSE),
|
|
m_bHeightDefined(FALSE),
|
|
m_iColSpan(1),
|
|
m_iRowSpan(1),
|
|
m_iRow(0),
|
|
m_X(0),
|
|
m_Y(0),
|
|
m_pSaveParent(0),
|
|
m_pSaveNext(0),
|
|
m_bSaveWidthPercent(FALSE),
|
|
m_bSaveHeightPercent(FALSE),
|
|
m_bSaveWidthDefined(FALSE),
|
|
m_bSaveHeightDefined(FALSE),
|
|
m_bSelected(FALSE),
|
|
m_bSpecialSelected(FALSE),
|
|
m_bDeleted(FALSE)
|
|
{
|
|
}
|
|
|
|
CEditTableCellElement::CEditTableCellElement(XP_Bool bIsHeader)
|
|
: CEditSubDocElement((CEditElement*) NULL, bIsHeader ? P_TABLE_HEADER : P_TABLE_DATA),
|
|
m_backgroundColor(),
|
|
m_iBackgroundSaveIndex(0),
|
|
m_pBackgroundImage(0),
|
|
m_iWidth(1),
|
|
m_iWidthPixels(1),
|
|
m_iHeight(1),
|
|
m_iHeightPixels(1),
|
|
m_bWidthPercent(FALSE),
|
|
m_bHeightPercent(FALSE),
|
|
m_bWidthDefined(FALSE),
|
|
m_bHeightDefined(FALSE),
|
|
m_iColSpan(1),
|
|
m_iRowSpan(1),
|
|
m_iRow(0),
|
|
m_X(0),
|
|
m_Y(0),
|
|
m_pSaveParent(0),
|
|
m_pSaveNext(0),
|
|
m_bSaveWidthPercent(FALSE),
|
|
m_bSaveHeightPercent(FALSE),
|
|
m_bSaveWidthDefined(FALSE),
|
|
m_bSaveHeightDefined(FALSE),
|
|
m_bSelected(FALSE),
|
|
m_bSpecialSelected(FALSE),
|
|
m_bDeleted(FALSE)
|
|
{
|
|
}
|
|
|
|
CEditTableCellElement::CEditTableCellElement(CEditElement *pParent, PA_Tag *pTag, int16 /*csid*/)
|
|
: CEditSubDocElement(pParent, pTag->type),
|
|
m_backgroundColor(),
|
|
m_iBackgroundSaveIndex(0),
|
|
m_pBackgroundImage(0),
|
|
m_iWidth(1),
|
|
m_iWidthPixels(1),
|
|
m_iHeight(1),
|
|
m_iHeightPixels(1),
|
|
m_bWidthPercent(FALSE),
|
|
m_bHeightPercent(FALSE),
|
|
m_bWidthDefined(FALSE),
|
|
m_bHeightDefined(FALSE),
|
|
m_iColSpan(1),
|
|
m_iRowSpan(1),
|
|
m_iRow(0),
|
|
m_X(0),
|
|
m_Y(0),
|
|
m_pSaveParent(0),
|
|
m_pSaveNext(0),
|
|
m_bSaveWidthPercent(FALSE),
|
|
m_bSaveHeightPercent(FALSE),
|
|
m_bSaveWidthDefined(FALSE),
|
|
m_bSaveHeightDefined(FALSE),
|
|
m_bSelected(FALSE),
|
|
m_bSpecialSelected(FALSE),
|
|
m_bDeleted(FALSE)
|
|
{
|
|
if( pTag ){
|
|
char *locked_buff;
|
|
|
|
PA_LOCK(locked_buff, char *, pTag->data );
|
|
if( locked_buff ){
|
|
SetTagData( locked_buff );
|
|
}
|
|
PA_UNLOCK(pTag->data);
|
|
}
|
|
}
|
|
|
|
CEditTableCellElement::CEditTableCellElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer)
|
|
: CEditSubDocElement(pStreamIn, pBuffer),
|
|
m_backgroundColor(),
|
|
m_iBackgroundSaveIndex(0),
|
|
m_pBackgroundImage(0),
|
|
m_iWidth(1),
|
|
m_iWidthPixels(1),
|
|
m_iHeight(1),
|
|
m_iHeightPixels(1),
|
|
m_bWidthPercent(FALSE),
|
|
m_bHeightPercent(FALSE),
|
|
m_iColSpan(1),
|
|
m_iRowSpan(1),
|
|
m_iRow(0),
|
|
m_X(0),
|
|
m_Y(0),
|
|
m_pSaveParent(0),
|
|
m_pSaveNext(0),
|
|
m_bSaveWidthPercent(FALSE),
|
|
m_bSaveHeightPercent(FALSE),
|
|
m_bSaveWidthDefined(FALSE),
|
|
m_bSaveHeightDefined(FALSE),
|
|
m_bSelected(FALSE),
|
|
m_bSpecialSelected(FALSE),
|
|
m_bDeleted(FALSE)
|
|
{
|
|
// We need this so we can accurately determine number
|
|
// of columns in stream
|
|
m_X = pStreamIn->ReadInt();
|
|
|
|
// Get width from stream -- needed for pasting tables/cells
|
|
// We must get CSID from buffer since element is not in doc yet
|
|
//TODO: Should we pass in CSID from paste stream instead?
|
|
PA_Tag* pTag = CEditElement::TagOpen(0);
|
|
EDT_TableCellData *pData = ParseParams( pTag, pBuffer->GetRAMCharSetID() );
|
|
if(pData)
|
|
{
|
|
m_bWidthDefined = pData->bWidthDefined;
|
|
m_bHeightDefined = pData->bHeightDefined;
|
|
|
|
// Save these since they are not saved in tag data if bWidthPercent = FALSE
|
|
m_iWidth = pData->iWidth;
|
|
m_bWidthPercent = pData->bWidthPercent;
|
|
if( !m_bWidthPercent )
|
|
m_iWidthPixels = m_iWidth;
|
|
|
|
m_iHeight = pData->iHeight;
|
|
m_bHeightPercent = pData->bHeightPercent;
|
|
if( !m_bHeightPercent )
|
|
m_iHeightPixels = m_iHeight;
|
|
|
|
EDT_FreeTableCellData(pData);
|
|
}
|
|
PA_FreeTag( pTag );
|
|
}
|
|
|
|
// We should never call Unlink for a table cell we are about
|
|
// to delete - it will get unlinked automatically.
|
|
// But in case someone does, we must unselect the cell here
|
|
// else it will fail to find the EditBuffer in the destructor
|
|
void CEditTableCellElement::Unlink(){
|
|
if( IsSelected() && GetEditBuffer() )
|
|
GetEditBuffer()->SelectCell(FALSE, NULL, this);
|
|
CEditElement::Unlink();
|
|
}
|
|
|
|
CEditTableCellElement::~CEditTableCellElement(){
|
|
// Be sure any cell deleted is not selected
|
|
if( IsSelected() && GetEditBuffer() )
|
|
GetEditBuffer()->SelectCell(FALSE, NULL, this);
|
|
|
|
delete m_pBackgroundImage;
|
|
}
|
|
|
|
XP_Bool CEditTableCellElement::IsTableCell(){
|
|
return TRUE;
|
|
}
|
|
|
|
EEditElementType CEditTableCellElement::GetElementType(){
|
|
return eTableCellElement;
|
|
}
|
|
|
|
ED_Alignment CEditTableCellElement::GetDefaultAlignment(){
|
|
return ED_ALIGN_DEFAULT;
|
|
}
|
|
|
|
XP_Bool CEditTableCellElement::IsTableData(){
|
|
return GetType() == P_TABLE_DATA;
|
|
}
|
|
|
|
void CEditTableCellElement::IncreaseColSpan(int32 iIncrease)
|
|
{
|
|
EDT_TableCellData *pData = GetData();
|
|
if( pData )
|
|
{
|
|
pData->iColSpan += iIncrease;
|
|
SetData(pData);
|
|
EDT_FreeTableCellData(pData);
|
|
}
|
|
}
|
|
|
|
void CEditTableCellElement::IncreaseRowSpan(int32 iIncrease)
|
|
{
|
|
EDT_TableCellData *pData = GetData();
|
|
if( pData )
|
|
{
|
|
pData->iRowSpan += iIncrease;
|
|
SetData(pData);
|
|
EDT_FreeTableCellData(pData);
|
|
}
|
|
}
|
|
|
|
void CEditTableCellElement::DecreaseColSpan(int32 iDecrease)
|
|
{
|
|
EDT_TableCellData *pData = GetData();
|
|
if( pData )
|
|
{
|
|
pData->iColSpan = max(1, pData->iColSpan - iDecrease);
|
|
SetData(pData);
|
|
EDT_FreeTableCellData(pData);
|
|
}
|
|
}
|
|
|
|
void CEditTableCellElement::DecreaseRowSpan(int32 iDecrease)
|
|
{
|
|
EDT_TableCellData *pData = GetData();
|
|
if( pData )
|
|
{
|
|
pData->iRowSpan = max(1, pData->iRowSpan - iDecrease);
|
|
SetData(pData);
|
|
EDT_FreeTableCellData(pData);
|
|
}
|
|
}
|
|
|
|
// Save size percent and defined params so we can much with them during resizing,
|
|
// and restore them later. Supply bWidthDefined and bHeightDefined
|
|
// for efficiency so we don't have to do GetData() here
|
|
void CEditTableCellElement::SaveSizeMode(XP_Bool bWidthDefined, XP_Bool bHeightDefined)
|
|
{
|
|
m_bSaveWidthPercent = m_bWidthPercent;
|
|
m_bSaveHeightPercent = m_bWidthPercent;
|
|
m_bSaveWidthDefined = bWidthDefined;
|
|
m_bSaveHeightDefined = bHeightDefined;
|
|
}
|
|
|
|
// This will restore saved width and height percent modes,
|
|
// readjust m_bWidth an m_bHeight, and call SetData to set tag data
|
|
// Note: no need to relayout -- we are just changing modes,
|
|
// not actual size of cell
|
|
void CEditTableCellElement::RestoreSizeMode(int32 iParentWidth, int32 iParentHeight)
|
|
{
|
|
EDT_TableCellData *pData = GetData();
|
|
|
|
if( iParentWidth == 0 )
|
|
iParentWidth = GetParentWidth();
|
|
if( iParentHeight == 0 )
|
|
iParentHeight = GetParentHeight();
|
|
|
|
if( pData )
|
|
{
|
|
if( m_bSaveWidthPercent )
|
|
{
|
|
// Convert existing Pixel value to % of parent size
|
|
pData->iWidth = (m_iWidthPixels * 100) / iParentWidth;
|
|
pData->iHeight = (m_iHeightPixels * 100) / iParentHeight;
|
|
} else {
|
|
// Converting to pixels is simple - just use the pixel value we already have
|
|
pData->iWidth = m_iWidthPixels;
|
|
pData->iHeight = m_iHeightPixels;
|
|
}
|
|
pData->bWidthDefined = m_bSaveWidthDefined;
|
|
pData->bHeightDefined = m_bSaveHeightDefined;
|
|
pData->bWidthPercent = m_bSaveWidthPercent;
|
|
pData->bHeightPercent = m_bSaveHeightPercent;
|
|
SetData(pData);
|
|
EDT_FreeTableCellData(pData);
|
|
}
|
|
}
|
|
|
|
// Calculate the Percent size from the iWidthPixels and iHeightPixels in pData
|
|
void CEditTableCellElement::CalcPercentWidth(EDT_TableCellData *pData)
|
|
{
|
|
if( pData && pData->bWidthDefined )
|
|
{
|
|
if( pData->bWidthPercent )
|
|
pData->iWidth = (pData->iWidthPixels * 100) / GetParentWidth();
|
|
else
|
|
pData->iWidth = pData->iWidthPixels;
|
|
}
|
|
}
|
|
|
|
void CEditTableCellElement::CalcPercentHeight(EDT_TableCellData *pData)
|
|
{
|
|
if( pData && pData->bHeightDefined )
|
|
{
|
|
if( pData->bHeightPercent )
|
|
pData->iHeight = (pData->iHeightPixels * 100) / GetParentHeight();
|
|
else
|
|
pData->iHeight = pData->iHeightPixels;
|
|
}
|
|
}
|
|
|
|
// Calculate the Pixel size from the iWidth and iHeight in pData
|
|
void CEditTableCellElement::CalcPixelWidth(EDT_TableCellData *pData)
|
|
{
|
|
if( pData && pData->bWidthDefined )
|
|
{
|
|
if( pData->bWidthPercent )
|
|
pData->iWidthPixels = (pData->iWidth * GetParentWidth()) / 100;
|
|
else
|
|
pData->iWidthPixels = pData->iWidth;
|
|
}
|
|
}
|
|
|
|
void CEditTableCellElement::CalcPixelHeight(EDT_TableCellData *pData)
|
|
{
|
|
if( pData && pData->bHeightDefined )
|
|
{
|
|
if( pData->bHeightPercent )
|
|
pData->iHeightPixels = (pData->iHeight * GetParentHeight()) / 100;
|
|
else
|
|
pData->iHeightPixels = pData->iHeight;
|
|
}
|
|
}
|
|
|
|
void CEditTableCellElement::SetSizeData(EDT_TableCellData * pData)
|
|
{
|
|
EDT_TableCellData * pCurrentData = GetData(0);
|
|
if( pData && pCurrentData )
|
|
{
|
|
pCurrentData->X = pData->X;
|
|
pCurrentData->Y = pData->Y;
|
|
pCurrentData->iRow = pData->iRow;
|
|
pCurrentData->bWidthDefined = pData->bWidthDefined;
|
|
pCurrentData->bWidthPercent = pData->bWidthPercent;
|
|
pCurrentData->iWidthPixels = pData->iWidthPixels;
|
|
CalcPercentWidth(pCurrentData);
|
|
pCurrentData->bHeightDefined = pData->bHeightDefined;
|
|
pCurrentData->bHeightPercent = pData->bHeightPercent;
|
|
pCurrentData->iHeightPixels = pData->iHeightPixels;
|
|
CalcPercentHeight(pCurrentData);
|
|
SetData(pCurrentData);
|
|
EDT_FreeTableCellData(pCurrentData);
|
|
}
|
|
}
|
|
|
|
void CEditTableCellElement::SetWidth(XP_Bool bWidthDefined, XP_Bool bWidthPercent, int32 iWidthPixels)
|
|
{
|
|
EDT_TableCellData * pData = GetData(0);
|
|
if( pData )
|
|
{
|
|
pData->bWidthDefined = bWidthDefined;
|
|
pData->bWidthPercent = bWidthPercent;
|
|
pData->iWidthPixels = iWidthPixels;
|
|
CalcPercentWidth(pData);
|
|
SetData(pData);
|
|
EDT_FreeTableCellData(pData);
|
|
}
|
|
}
|
|
|
|
void CEditTableCellElement::SetHeight(XP_Bool bHeightDefined, XP_Bool bHeightPercent, int32 iHeightPixels)
|
|
{
|
|
EDT_TableCellData * pData = GetData(0);
|
|
if( pData )
|
|
{
|
|
pData->bHeightDefined = bHeightDefined;
|
|
pData->bHeightPercent = bHeightPercent;
|
|
pData->iHeightPixels = iHeightPixels;
|
|
CalcPercentHeight(pData);
|
|
SetData(pData);
|
|
EDT_FreeTableCellData(pData);
|
|
}
|
|
}
|
|
|
|
// Set width of supplied cell and all other cells sharing or spanning the same right edge
|
|
// Also sets the bWidthDefined and bWidthPercent modes
|
|
void CEditTableCellElement::SetColumnWidthRight(CEditTableElement *pTable, LO_Element *pLoCell, EDT_TableCellData *pData)
|
|
{
|
|
if( !pTable || !pLoCell || !pData || pData->iWidthPixels <= 0 )
|
|
return;
|
|
|
|
// Get LoTable to start scanning through cells
|
|
LO_Element *pLoElement = (LO_Element*)lo_GetParentTable(NULL, pLoCell);
|
|
if( !pLoElement )
|
|
return;
|
|
|
|
// Amount of change in supplied cell
|
|
int32 iDeltaWidth = pData->iWidthPixels - pLoCell->lo_any.width;
|
|
int32 iRightEdge = pLoCell->lo_cell.x + pLoCell->lo_cell.width;
|
|
|
|
// Set the width for all cells sharing or spanning the right edge of cell resized
|
|
while (pLoElement && pLoElement->type != LO_LINEFEED )
|
|
{
|
|
if( pLoElement->type == LO_CELL &&
|
|
pLoElement->lo_cell.x < iRightEdge &&
|
|
(pLoElement->lo_cell.x + pLoElement->lo_cell.width) >= iRightEdge )
|
|
{
|
|
CEditTableCellElement *pEdCell =
|
|
(CEditTableCellElement*)edt_GetTableElementFromLO_Element( pLoElement, LO_CELL );
|
|
|
|
if( pEdCell )
|
|
{
|
|
pLoElement->lo_cell.width = max(1, pLoElement->lo_cell.width + iDeltaWidth);
|
|
// Compensate for cell padding, border, and COLSPAN to get
|
|
// the value to use for WIDTH tag param
|
|
pEdCell->SetWidth( pData->bWidthDefined, pData->bWidthPercent,
|
|
lo_GetCellTagWidth(pLoElement) );
|
|
}
|
|
}
|
|
pLoElement = pLoElement->lo_any.next;
|
|
}
|
|
EDT_TableData *pTableData = pTable->GetData();
|
|
if( pTableData )
|
|
{
|
|
if( pTableData->bWidthDefined )
|
|
{
|
|
// Force pixel mode and adjust the width of parent table
|
|
// If we don't, a table width in Percent mode will fight column size change
|
|
pTableData->iWidthPixels = max(1,pTableData->iWidthPixels + iDeltaWidth);
|
|
pTableData->bWidthPercent = FALSE;
|
|
pTableData->iWidth = pTableData->iWidthPixels;
|
|
pTable->SetData(pTableData);
|
|
}
|
|
EDT_FreeTableData(pTableData);
|
|
}
|
|
}
|
|
|
|
// Set height of all cells sharing or spanning the bottom edge
|
|
// Also sets the bWidthDefined and bWidthPercent modes
|
|
void CEditTableCellElement::SetRowHeightBottom(CEditTableElement *pTable, LO_Element *pLoCell, EDT_TableCellData *pData)
|
|
{
|
|
if( !pTable || !pLoCell || !pData || pData->iHeightPixels <= 0 )
|
|
return;
|
|
|
|
// Get LoTable to start scanning through cells
|
|
LO_Element *pLoElement = (LO_Element*)lo_GetParentTable(NULL, pLoCell);
|
|
if( !pLoElement )
|
|
return;
|
|
|
|
// Amount of change in supplied cell - use this for all cells we will resize
|
|
int32 iDeltaHeight = pData->iHeightPixels - pLoCell->lo_any.height;
|
|
int32 iBottomEdge = pLoCell->lo_cell.y + pLoCell->lo_cell.height;
|
|
|
|
// Set the height for all cells sharing or spanning the right edge of cell resized
|
|
while (pLoElement && pLoElement->type != LO_LINEFEED )
|
|
{
|
|
if( pLoElement->type == LO_CELL &&
|
|
pLoElement->lo_cell.y < iBottomEdge &&
|
|
(pLoElement->lo_cell.y + pLoElement->lo_cell.height) >= iBottomEdge )
|
|
{
|
|
CEditTableCellElement *pEdCell =
|
|
(CEditTableCellElement*)edt_GetTableElementFromLO_Element( pLoElement, LO_CELL );
|
|
|
|
if( pEdCell )
|
|
{
|
|
pLoElement->lo_cell.height = max(1, pLoElement->lo_cell.height + iDeltaHeight);
|
|
pEdCell->SetHeight( pData->bHeightDefined, pData->bHeightPercent,
|
|
lo_GetCellTagHeight(pLoElement) );
|
|
}
|
|
}
|
|
pLoElement = pLoElement->lo_any.next;
|
|
}
|
|
}
|
|
|
|
// Set width of supplied cell and all other cells sharing or spanning the same left edge
|
|
// Also sets the bWidthDefined and bWidthPercent modes
|
|
void CEditTableCellElement::SetColumnWidthLeft(CEditTableElement *pTable, CEditTableCellElement *pCell, EDT_TableCellData *pData)
|
|
{
|
|
if( !pTable || !pCell || !pData || pData->iWidthPixels <= 0 )
|
|
return;
|
|
|
|
// Amount of change in supplied cell
|
|
int32 iDeltaWidth = pData->iWidthPixels - pCell->GetWidth();
|
|
|
|
// TRUE means we include cells that span over cell's left edge,
|
|
// not just start at that value.
|
|
pCell = pCell->GetFirstCellInColumn(pCell->GetX(), TRUE);
|
|
while( pCell )
|
|
{
|
|
// Set width - be sure it is at least 1
|
|
pCell->SetWidth( pData->bWidthDefined, pData->bWidthPercent,
|
|
max(1, pCell->GetWidth() + iDeltaWidth) );
|
|
pCell = pCell->GetNextCellInColumn();
|
|
}
|
|
}
|
|
|
|
// Set height of all cells sharing or spanning the top edge
|
|
// Also sets the bWidthDefined and bWidthPercent modes
|
|
void CEditTableCellElement::SetRowHeightTop(CEditTableElement *pTable, CEditTableCellElement *pCell, EDT_TableCellData *pData)
|
|
{
|
|
if( !pTable || !pCell || !pData || pData->iHeightPixels <= 0 )
|
|
return;
|
|
|
|
// Amount of change in supplied cell - use this for all cells we will resize
|
|
int32 iDeltaHeight = pData->iHeightPixels - pCell->GetHeight();
|
|
|
|
// TRUE means we include cells that span over current cell's Y,
|
|
// not just start at Y
|
|
pCell = pCell->GetFirstCellInRow(pCell->GetY(), TRUE);
|
|
while( pCell )
|
|
{
|
|
// Set height - be sure it is at least 1
|
|
pCell->SetHeight( pData->bHeightDefined, pData->bHeightPercent,
|
|
max(1, pCell->GetHeight() + iDeltaHeight) );
|
|
pCell = pCell->GetNextCellInRow();
|
|
}
|
|
}
|
|
|
|
void CEditTableCellElement::StreamOut(IStreamOut *pOut){
|
|
CEditElement::StreamOut( pOut );
|
|
// We write our X so we can accurately determine
|
|
// the number of columns when streaming in (pasting)
|
|
pOut->WriteInt( m_X );
|
|
}
|
|
|
|
void CEditTableCellElement::SetData( EDT_TableCellData *pData ){
|
|
// bHeader is actually stored as the tag data
|
|
if ( pData->bHeader ) {
|
|
SetType(P_TABLE_HEADER);
|
|
}
|
|
else {
|
|
SetType(P_TABLE_DATA);
|
|
}
|
|
//TODO: Account for interaction between default align and Header style
|
|
char *pNew = 0;
|
|
if( pData->align != ED_ALIGN_DEFAULT ){
|
|
pNew = PR_sprintf_append( pNew, "ALIGN=%s ", EDT_AlignString(pData->align) );
|
|
}
|
|
if( pData->valign != ED_ALIGN_DEFAULT ){
|
|
pNew = PR_sprintf_append( pNew, "VALIGN=%s ", EDT_AlignString(pData->valign) );
|
|
}
|
|
if ( pData->iColSpan != 1 ){
|
|
pNew = PR_sprintf_append( pNew, "COLSPAN=\"%d\" ", pData->iColSpan );
|
|
}
|
|
if ( pData->iRowSpan != 1 ){
|
|
pNew = PR_sprintf_append( pNew, "ROWSPAN=\"%d\" ", pData->iRowSpan );
|
|
}
|
|
if ( pData->bNoWrap ){
|
|
pNew = PR_sprintf_append( pNew, "NOWRAP " );
|
|
}
|
|
if( pData->bWidthDefined ){
|
|
if( pData->bWidthPercent ){
|
|
pNew = PR_sprintf_append( pNew, "WIDTH=\"%ld%%\" ",
|
|
(long)min(100,max(1,pData->iWidth)) );
|
|
}
|
|
else {
|
|
pNew = PR_sprintf_append( pNew, "WIDTH=\"%ld\" ", (long)pData->iWidth );
|
|
}
|
|
}
|
|
if( pData->bHeightDefined ){
|
|
if( pData->bHeightPercent ){
|
|
pNew = PR_sprintf_append( pNew, "HEIGHT=\"%ld%%\" ",
|
|
(long)min(100,max(1, pData->iHeight)) );
|
|
}
|
|
else {
|
|
pNew = PR_sprintf_append( pNew, "HEIGHT=\"%ld\" ", (long)pData->iHeight );
|
|
}
|
|
}
|
|
|
|
if ( pData->pColorBackground ) {
|
|
SetBackgroundColor(EDT_LO_COLOR(pData->pColorBackground));
|
|
pNew = PR_sprintf_append( pNew, "BGCOLOR=\"#%06lX\" ", GetBackgroundColor().GetAsLong() );
|
|
}
|
|
else {
|
|
SetBackgroundColor(ED_Color::GetUndefined());
|
|
}
|
|
|
|
if ( pData->pBackgroundImage ) {
|
|
SetBackgroundImage(pData->pBackgroundImage);
|
|
pNew = PR_sprintf_append( pNew, "BACKGROUND=\"%s\" ", pData->pBackgroundImage );
|
|
}
|
|
else {
|
|
SetBackgroundImage(0);
|
|
}
|
|
if ( pData->bBackgroundNoSave) {
|
|
pNew = PR_sprintf_append( pNew, "NOSAVE " );
|
|
}
|
|
|
|
if( pData->pExtra ){
|
|
pNew = PR_sprintf_append( pNew, "%s ", pData->pExtra );
|
|
}
|
|
|
|
if( pNew ){
|
|
pNew = PR_sprintf_append( pNew, ">" );
|
|
}
|
|
SetTagData( pNew );
|
|
if ( pNew ) {
|
|
free(pNew);
|
|
}
|
|
|
|
// Save these since they are not saved in tag data if bWidthPercent = FALSE
|
|
m_X = pData->X;
|
|
m_Y = pData->Y;
|
|
|
|
m_bWidthDefined = pData->bWidthDefined;
|
|
m_bHeightDefined = pData->bHeightDefined;
|
|
|
|
m_iWidthPixels = pData->iWidthPixels;
|
|
m_iHeightPixels = pData->iHeightPixels;
|
|
|
|
m_iWidth = pData->iWidth;
|
|
m_bWidthPercent = pData->bWidthPercent;
|
|
|
|
m_iHeight = pData->iHeight;
|
|
m_bHeightPercent = pData->bHeightPercent;
|
|
|
|
m_iRow = pData->iRow;
|
|
m_iColSpan = pData->iColSpan;
|
|
m_iRowSpan = pData->iRowSpan;
|
|
}
|
|
|
|
// Since Getting/Setting data is costly (lots of string manipulation)
|
|
// use this when efficiency is important and you need just size data
|
|
// For even more efficiency, supply the TableCellData to avoid
|
|
// alloc/delete for large number of cells
|
|
EDT_TableCellData *CEditTableCellElement::GetSizeData(EDT_TableCellData *pData)
|
|
{
|
|
if( pData == NULL )
|
|
{
|
|
pData = XP_NEW( EDT_TableCellData );
|
|
if( pData )
|
|
XP_MEMSET( pData, 0, sizeof(EDT_TableCellData));
|
|
}
|
|
if( pData == 0 )
|
|
return NULL;
|
|
|
|
|
|
// Get just our member variables
|
|
pData->bWidthDefined = m_bWidthDefined;
|
|
pData->bHeightDefined = m_bHeightDefined;
|
|
pData->bWidthPercent = m_bWidthPercent;
|
|
pData->bHeightPercent = m_bHeightPercent;
|
|
pData->iWidth = m_iWidth;
|
|
pData->iWidthPixels = m_iWidthPixels;
|
|
pData->iHeight = m_iHeight;
|
|
pData->iHeightPixels = m_iHeightPixels;
|
|
pData->X = m_X;
|
|
pData->Y = m_Y;
|
|
pData->iRow = m_iRow;
|
|
|
|
return pData;
|
|
}
|
|
|
|
EDT_TableCellData* CEditTableCellElement::GetData( int16 csid )
|
|
{
|
|
//TODO: Account for interaction between default align and Header style
|
|
EDT_TableCellData *pRet;
|
|
PA_Tag* pTag = TagOpen(0);
|
|
// Must supply csid if cell is not already in the document
|
|
if( csid == 0 )
|
|
csid = GetWinCSID();
|
|
|
|
// Get params stored in the HTML tag struct
|
|
pRet = ParseParams( pTag, csid );
|
|
PA_FreeTag( pTag );
|
|
// Return the actual X, Y, width, and height
|
|
// as determined by table layout algorithm
|
|
// This is set correctly event if bWidthDefined = FALSE
|
|
pRet->X = m_X;
|
|
pRet->Y = m_Y;
|
|
pRet->iRow = m_iRow;
|
|
pRet->iWidth = m_iWidth;
|
|
pRet->iWidthPixels = m_iWidthPixels;
|
|
pRet->iHeight = m_iHeight;
|
|
pRet->iHeightPixels = m_iHeightPixels;
|
|
|
|
pRet->iRow = m_iRow;
|
|
pRet->iColSpan = m_iColSpan;
|
|
pRet->iRowSpan = m_iRowSpan;
|
|
pRet->mask = -1;
|
|
pRet->iSelectionType = ED_HIT_NONE;
|
|
pRet->iSelectedCount = 0;
|
|
return pRet;
|
|
}
|
|
|
|
// Include all borders, spacing, and padding up to the next cell
|
|
int32 CEditTableCellElement::GetFullWidth(CEditTableElement *pTable)
|
|
{
|
|
if( !pTable )
|
|
pTable = GetParentTable();
|
|
if( !pTable )
|
|
return 0;
|
|
|
|
return (m_iWidthPixels + 2*(pTable->GetCellBorder() + pTable->GetCellPadding()) +
|
|
m_iColSpan * pTable->GetCellSpacing() );
|
|
}
|
|
|
|
int32 CEditTableCellElement::GetFullHeight(CEditTableElement *pTable)
|
|
{
|
|
if( !pTable )
|
|
pTable = GetParentTable();
|
|
if( !pTable )
|
|
return 0;
|
|
|
|
return (m_iHeightPixels + 2*(pTable->GetCellBorder() + pTable->GetCellPadding()) +
|
|
m_iRowSpan * pTable->GetCellSpacing() );
|
|
}
|
|
|
|
static XP_Bool edt_CompareLoColors( LO_Color *pLoColor1, LO_Color *pLoColor2 )
|
|
{
|
|
// One or the other doesn't have color,
|
|
// they are different
|
|
if( (!pLoColor1 && pLoColor2) ||
|
|
(pLoColor1 && !pLoColor2 ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
// Neither has a color - the same
|
|
if( !pLoColor1 && !pLoColor2 )
|
|
return TRUE;
|
|
|
|
// Any color is different
|
|
if( pLoColor1->red != pLoColor2->red ||
|
|
pLoColor1->green != pLoColor2->green ||
|
|
pLoColor1->blue != pLoColor2->blue )
|
|
{
|
|
return FALSE;
|
|
}
|
|
// All must be the same
|
|
return TRUE;
|
|
}
|
|
|
|
static XP_Bool edt_CompareStrings( char *pString1, char *pString2, XP_Bool bFilenameCompare )
|
|
{
|
|
// One or the other doesn't exist,
|
|
// they are different
|
|
if( (!pString1 && pString2) ||
|
|
(pString1 && !pString2 ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
// Neither exists - the same
|
|
if( !pString1 && !pString2 )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// Compare the strings
|
|
if( bFilenameCompare )
|
|
{
|
|
// This compare pays NO attention to case differences for Windows only
|
|
return (0 == XP_FILENAMECMP(pString1, pString2));
|
|
}
|
|
|
|
return (0 == XP_STRCMP(pString1, pString2));
|
|
}
|
|
|
|
void CEditTableCellElement::MaskData( EDT_TableCellData *pData )
|
|
{
|
|
if( !pData )
|
|
return;
|
|
|
|
EDT_TableCellData *pCurrentData = GetData();
|
|
|
|
// Change data only for attributes whose bit is set in data mask
|
|
if( (pData->mask & CF_ALIGN) &&
|
|
pCurrentData->align != pData->align )
|
|
{
|
|
pData->mask &= ~CF_ALIGN;
|
|
}
|
|
|
|
if( (pData->mask & CF_VALIGN) &&
|
|
pCurrentData->valign != pData->valign )
|
|
{
|
|
pData->mask &= ~CF_VALIGN;
|
|
}
|
|
|
|
if( (pData->mask & CF_COLSPAN) &&
|
|
pCurrentData->iColSpan != pData->iColSpan )
|
|
{
|
|
pData->mask &= ~CF_COLSPAN;
|
|
}
|
|
|
|
if( (pData->mask & CF_ROWSPAN) &&
|
|
pCurrentData->iRowSpan != pData->iRowSpan )
|
|
{
|
|
pData->mask &= ~CF_ROWSPAN;
|
|
}
|
|
|
|
if( (pData->mask & CF_HEADER) &&
|
|
pCurrentData->bHeader != pData->bHeader )
|
|
{
|
|
pData->mask &= ~CF_HEADER;
|
|
}
|
|
|
|
if( (pData->mask & CF_NOWRAP) &&
|
|
pCurrentData->bNoWrap != pData->bNoWrap )
|
|
{
|
|
pData->mask &= ~CF_NOWRAP;
|
|
}
|
|
|
|
if( (pData->mask & CF_BACK_NOSAVE) &&
|
|
pCurrentData->bBackgroundNoSave != pData->bBackgroundNoSave )
|
|
{
|
|
pData->mask &= ~CF_BACK_NOSAVE;
|
|
}
|
|
|
|
if( (pData->mask & CF_WIDTH) &&
|
|
(pCurrentData->bWidthDefined != pData->bWidthDefined ||
|
|
pCurrentData->iWidth != pData->iWidth ||
|
|
pCurrentData->bWidthPercent != pData->bWidthPercent) )
|
|
{
|
|
pData->mask &= ~CF_WIDTH;
|
|
}
|
|
|
|
if( (pData->mask & CF_HEIGHT) &&
|
|
(pCurrentData->bHeightDefined != pData->bHeightDefined ||
|
|
pCurrentData->iHeight != pData->iHeight ||
|
|
pCurrentData->bHeightPercent != pData->bHeightPercent) )
|
|
{
|
|
pData->mask &= ~CF_HEIGHT;
|
|
}
|
|
|
|
if( (pData->mask & CF_BACK_COLOR) &&
|
|
!edt_CompareLoColors(pCurrentData->pColorBackground, pData->pColorBackground) )
|
|
{
|
|
pData->mask &= ~CF_BACK_COLOR;
|
|
}
|
|
|
|
// This pays NO attention to case differences for Windows only
|
|
if( (pData->mask & CF_BACK_IMAGE) &&
|
|
!edt_CompareStrings(pCurrentData->pBackgroundImage, pData->pBackgroundImage, TRUE) )
|
|
{
|
|
pData->mask &= ~CF_BACK_IMAGE;
|
|
}
|
|
|
|
// This pays attention to case differences
|
|
if( (pData->mask & CF_EXTRA_HTML) &&
|
|
!edt_CompareStrings(pCurrentData->pExtra, pData->pExtra, FALSE) )
|
|
{
|
|
pData->mask &= ~CF_EXTRA_HTML;
|
|
}
|
|
}
|
|
|
|
// Add up widths of all cells in the first row of table
|
|
// This is available total width for "Percent of Table" calculation
|
|
int32 CEditTableCellElement::GetParentWidth()
|
|
{
|
|
// Don't return 0 to avoid divide by zero errors
|
|
int32 iParentWidth = 1;
|
|
|
|
CEditTableElement *pTable = GetParentTable();
|
|
if( pTable )
|
|
{
|
|
CEditTableCellElement *pCell = pTable->GetFirstCell();
|
|
while( pCell && pCell->IsTableCell() )
|
|
{
|
|
iParentWidth += pCell->GetWidth(); // TODO: SHOULD WE USE GetFullWidth()???
|
|
pCell = (CEditTableCellElement*)(pCell->GetNextSibling());
|
|
}
|
|
}
|
|
return iParentWidth;
|
|
}
|
|
|
|
int32 CEditTableCellElement::GetParentHeight()
|
|
{
|
|
// Don't return 0 to avoid divide by zero errors
|
|
int32 iParentHeight = 1;
|
|
|
|
CEditTableElement *pTable = GetParentTable();
|
|
if( pTable )
|
|
{
|
|
CEditTableCellElement *pCell = pTable->GetFirstCell();
|
|
while( pCell )
|
|
{
|
|
iParentHeight += pCell->GetHeight();
|
|
pCell = pTable->GetNextCellInColumn(pCell);
|
|
}
|
|
}
|
|
return iParentHeight;
|
|
}
|
|
|
|
static char *tableCellParams[] = {
|
|
PARAM_ALIGN,
|
|
PARAM_VALIGN,
|
|
PARAM_COLSPAN,
|
|
PARAM_ROWSPAN,
|
|
PARAM_NOWRAP,
|
|
PARAM_WIDTH,
|
|
PARAM_HEIGHT,
|
|
PARAM_BGCOLOR,
|
|
PARAM_BACKGROUND,
|
|
PARAM_NOSAVE,
|
|
0
|
|
};
|
|
|
|
EDT_TableCellData* CEditTableCellElement::ParseParams( PA_Tag *pTag, int16 csid ){
|
|
EDT_TableCellData *pData = NewData();
|
|
|
|
ED_Alignment align;
|
|
|
|
align = edt_FetchParamAlignment( pTag, ED_ALIGN_DEFAULT, FALSE, csid );
|
|
if( align == ED_ALIGN_RIGHT || align == ED_ALIGN_LEFT || align == ED_ALIGN_ABSCENTER ){
|
|
pData->align = align;
|
|
}
|
|
|
|
align = edt_FetchParamAlignment( pTag, ED_ALIGN_DEFAULT, TRUE, csid );
|
|
if( align == ED_ALIGN_ABSTOP || align == ED_ALIGN_ABSBOTTOM || align == ED_ALIGN_ABSCENTER
|
|
|| align == ED_ALIGN_BASELINE ){
|
|
pData->valign = align;
|
|
}
|
|
|
|
pData->iColSpan = edt_FetchParamInt(pTag, PARAM_COLSPAN, 1, csid);
|
|
pData->iRowSpan = edt_FetchParamInt(pTag, PARAM_ROWSPAN, 1, csid);
|
|
m_iColSpan = pData->iColSpan;
|
|
m_iRowSpan = pData->iRowSpan;
|
|
pData->bHeader = GetType() == P_TABLE_HEADER;
|
|
pData->bNoWrap = edt_FetchParamBoolExist(pTag, PARAM_NOWRAP, csid );
|
|
|
|
pData->bWidthDefined = edt_FetchDimension( pTag, PARAM_WIDTH,
|
|
&pData->iWidth, &pData->bWidthPercent,
|
|
100, TRUE, csid );
|
|
pData->bHeightDefined = edt_FetchDimension( pTag, PARAM_HEIGHT,
|
|
&pData->iHeight, &pData->bHeightPercent,
|
|
100, TRUE, csid );
|
|
|
|
// This and CEditBuffer::FixupTableData() are the only
|
|
// places member variable should be set
|
|
// If width and/or height are NOT defined, use the
|
|
// "bPercent" values set by any previous SetData() calls
|
|
if( pData->bWidthDefined )
|
|
m_iWidth = pData->iWidth;
|
|
else
|
|
pData->bWidthPercent = m_bWidthPercent;
|
|
|
|
if( pData->bHeightDefined )
|
|
m_iHeight = pData->iHeight;
|
|
else
|
|
pData->bHeightPercent = m_bHeightPercent;
|
|
|
|
pData->pColorBackground = edt_MakeLoColor(edt_FetchParamColor( pTag, PARAM_BGCOLOR, csid ));
|
|
pData->pBackgroundImage = edt_FetchParamString( pTag, PARAM_BACKGROUND, csid );
|
|
pData->bBackgroundNoSave = edt_FetchParamBoolExist( pTag, PARAM_NOSAVE, csid );
|
|
pData->pExtra = edt_FetchParamExtras( pTag, tableCellParams, csid );
|
|
return pData;
|
|
}
|
|
|
|
EDT_TableCellData* CEditTableCellElement::NewData(){
|
|
EDT_TableCellData *pData = XP_NEW( EDT_TableCellData );
|
|
if( pData == 0 ){
|
|
// throw();
|
|
return pData;
|
|
}
|
|
pData->align = ED_ALIGN_DEFAULT;
|
|
pData->valign = ED_ALIGN_ABSTOP; //ED_ALIGN_TOP; // was ED_ALIGN_DEFAULT; this is centerred - stupid!
|
|
pData->iColSpan = 1;
|
|
pData->iRowSpan = 1;
|
|
pData->bHeader = FALSE;
|
|
pData->bNoWrap = FALSE;
|
|
pData->bWidthDefined = FALSE;
|
|
pData->bWidthPercent = FALSE;
|
|
pData->iWidth = 1;
|
|
pData->iWidthPixels = 1;
|
|
pData->iRow = 0;
|
|
pData->iHeight = 1;
|
|
pData->iHeightPixels = 1;
|
|
pData->bHeightDefined = FALSE;
|
|
pData->bHeightPercent = FALSE;
|
|
pData->pColorBackground = 0;
|
|
pData->pBackgroundImage = 0;
|
|
pData->bBackgroundNoSave = FALSE;
|
|
pData->pExtra = 0;
|
|
pData->mask = -1;
|
|
pData->iSelectionType = ED_HIT_NONE;
|
|
pData->iSelectedCount = 0;
|
|
pData->X = 0;
|
|
pData->Y = 0;
|
|
return pData;
|
|
}
|
|
|
|
void CEditTableCellElement::FreeData( EDT_TableCellData *pData ){
|
|
if( pData->pColorBackground ) XP_FREE( pData->pColorBackground );
|
|
if( pData->pBackgroundImage ) XP_FREE( pData->pBackgroundImage );
|
|
if( pData->pExtra ) XP_FREE( pData->pExtra );
|
|
XP_FREE( pData );
|
|
}
|
|
|
|
void CEditTableCellElement::SetBackgroundColor( ED_Color iColor ){
|
|
m_backgroundColor = iColor;
|
|
}
|
|
|
|
ED_Color CEditTableCellElement::GetBackgroundColor(){
|
|
return m_backgroundColor;
|
|
}
|
|
|
|
void CEditTableCellElement::SetBackgroundImage( char* pImage ){
|
|
delete m_pBackgroundImage;
|
|
m_pBackgroundImage = 0;
|
|
if ( pImage ) {
|
|
m_pBackgroundImage = XP_STRDUP(pImage);
|
|
}
|
|
}
|
|
|
|
char* CEditTableCellElement::GetBackgroundImage(){
|
|
return m_pBackgroundImage;
|
|
}
|
|
|
|
LO_CellStruct* CEditTableCellElement::GetLoCell()
|
|
{
|
|
// Find the first leaf element in the cell since
|
|
// it will have pointer to LO_Element
|
|
CEditElement *pLeaf = FindNextElement(&CEditElement::FindLeafAll,0 );
|
|
if( pLeaf )
|
|
{
|
|
// Only leaf edit elements have their pointers saved in LO_Elements
|
|
LO_Element *pLoElement = pLeaf->Leaf()->GetLayoutElement();
|
|
if( pLoElement )
|
|
{
|
|
// Find enclosing LO cell
|
|
LO_CellStruct *pLoCell = lo_GetParentCell(GetEditBuffer()->m_pContext, pLoElement);
|
|
// We shoul ALWAYS find a cell
|
|
XP_ASSERT(pLoCell);
|
|
return pLoCell;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
CEditTableCellElement* CEditTableCellElement::GetPreviousCellInTable(intn *pRowCounter)
|
|
{
|
|
// Simplest case is previous cell in the row
|
|
CEditElement *pPreviousCell = GetPreviousSibling();
|
|
if( !pPreviousCell )
|
|
{
|
|
// Get the last child cell of the previous row
|
|
if( GetParent() && GetParent()->GetPreviousSibling() )
|
|
pPreviousCell = GetParent()->GetPreviousSibling()->GetLastChild();
|
|
#ifdef DEBUG
|
|
if( pPreviousCell ) XP_ASSERT(pPreviousCell->IsTableCell());
|
|
#endif
|
|
// Tell caller we wrapped to the previous row
|
|
if( pRowCounter && pPreviousCell )
|
|
(*pRowCounter)--;
|
|
}
|
|
return (CEditTableCellElement*)pPreviousCell;
|
|
}
|
|
|
|
CEditTableCellElement* CEditTableCellElement::GetNextCellInTable(intn *pRowCounter)
|
|
{
|
|
// Simplest case is next cell in the row
|
|
CEditElement *pNextCell = GetNextSibling();
|
|
if( !pNextCell )
|
|
{
|
|
// Get the first child cell of the next row
|
|
if( GetParent() && GetParent()->GetNextSibling() )
|
|
pNextCell = GetParent()->GetNextSibling()->GetChild();
|
|
|
|
// If the next child is not a cell,
|
|
// we hit a table caption at the end of the table
|
|
// (its the sibling after the last row in the table)
|
|
// so there's no more cells
|
|
if( pNextCell && !pNextCell->IsTableCell() )
|
|
return 0;
|
|
|
|
// Tell caller we wrapped to the next row
|
|
if( pRowCounter && pNextCell )
|
|
(*pRowCounter)++;
|
|
}
|
|
return (CEditTableCellElement*)pNextCell;
|
|
}
|
|
|
|
CEditTableCellElement* CEditTableCellElement::GetNextCellInLogicalRow()
|
|
{
|
|
CEditElement *pNextCell = GetNextSibling();
|
|
if( pNextCell && pNextCell->IsTableCell() )
|
|
return pNextCell->TableCell();
|
|
|
|
return NULL;
|
|
}
|
|
|
|
CEditTableCellElement* CEditTableCellElement::GetFirstCellInRow(int32 Y, XP_Bool bSpan)
|
|
{
|
|
// No Y supplied - just get it from the cell
|
|
if( Y == -1 )
|
|
Y = GetY();
|
|
|
|
CEditTableElement *pTable = GetParentTable();
|
|
if( pTable )
|
|
return pTable->GetFirstCellInRow(Y, bSpan);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
CEditTableCellElement* CEditTableCellElement::GetFirstCellInColumn(int32 X, XP_Bool bSpan)
|
|
{
|
|
// No X supplied - just get it from the cell
|
|
if( X == -1 )
|
|
X = GetX();
|
|
|
|
CEditTableElement *pTable = GetParentTable();
|
|
if( pTable )
|
|
return pTable->GetFirstCellInColumn(X, bSpan);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
CEditTableCellElement* CEditTableCellElement::GetNextCellInRow(CEditTableCellElement *pCell)
|
|
{
|
|
CEditTableElement *pTable = GetParentTable();
|
|
if( pTable )
|
|
return pTable->GetNextCellInRow(pCell);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
CEditTableCellElement* CEditTableCellElement::GetNextCellInColumn(CEditTableCellElement *pCell)
|
|
{
|
|
CEditTableElement *pTable = GetParentTable();
|
|
if( pTable )
|
|
return pTable->GetNextCellInColumn(pCell);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
XP_Bool CEditTableCellElement::AllCellsInColumnAreSelected()
|
|
{
|
|
CEditTableCellElement *pCell = GetFirstCellInColumn();
|
|
if( !pCell ) // Unlikely
|
|
{
|
|
XP_ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
while( pCell )
|
|
{
|
|
if( !pCell->IsSelected() )
|
|
return FALSE;
|
|
|
|
pCell = GetNextCellInColumn();
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
XP_Bool CEditTableCellElement::AllCellsInRowAreSelected()
|
|
{
|
|
CEditTableCellElement *pCell = GetFirstCellInRow();
|
|
if( !pCell ) // Unlikely
|
|
{
|
|
XP_ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
while( pCell )
|
|
{
|
|
if( !pCell->IsSelected() )
|
|
return FALSE;
|
|
|
|
pCell = GetNextCellInRow();
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
XP_Bool CEditTableCellElement::IsEmpty()
|
|
{
|
|
CEditElement *pElement = GetFirstMostChild();
|
|
|
|
// Should never happen, but if it does,
|
|
// I guess we can consider it empty!
|
|
if( !pElement )
|
|
return TRUE;
|
|
|
|
// We are empty if no more sibling containers
|
|
// and cell only has an empty string or
|
|
// a single space in the string (used to force showing borders in browser)
|
|
if( GetChild()->GetNextSibling() == 0 &&
|
|
pElement->IsText() && pElement->GetNextSibling() == 0 )
|
|
{
|
|
char *pText = pElement->Text()->GetText();
|
|
// We should NEVER be missing text
|
|
if( !pText )
|
|
return TRUE;
|
|
|
|
int iLen = XP_STRLEN(pText);
|
|
if( iLen == 0 || (iLen == 1 && pText[0] == ' ') )
|
|
return TRUE;
|
|
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void CEditTableCellElement::SplitCell()
|
|
{
|
|
CEditBuffer *pBuffer = GetEditBuffer();
|
|
if( pBuffer == NULL || (m_iColSpan == 0 && m_iRowSpan == 0) )
|
|
return;
|
|
|
|
pBuffer->SetFillNewCellWithSpace();
|
|
CEditTableCellElement *pNewCell;
|
|
int32 iNewRows = m_iRowSpan - 1;
|
|
int32 iNewCells = m_iColSpan - 1;
|
|
intn iNextRow = m_iRow + 1;
|
|
CEditTableRowElement *pCurrentRow = (CEditTableRowElement*)GetParent();
|
|
XP_ASSERT(pCurrentRow && pCurrentRow->IsTableRow());
|
|
CEditTableRowElement *pRow = pCurrentRow;
|
|
|
|
// Copy current cell's color and background image to all new cells
|
|
EDT_TableCellData *pData = GetData();
|
|
EDT_TableCellData *pNewData = NewData();
|
|
XP_ASSERT(pData && pNewData);
|
|
if( !pData || !pNewData )
|
|
return;
|
|
pData->mask = CF_BACK_COLOR | CF_BACK_IMAGE;
|
|
edt_CopyTableCellData(pNewData, pData);
|
|
|
|
// Insert new cells in the current row if needed
|
|
for( int i = 0; i < iNewCells; i++ )
|
|
{
|
|
pNewCell = new CEditTableCellElement;
|
|
pNewCell->InsertAfter(this);
|
|
pNewCell->FinishedLoad(pBuffer);
|
|
// Set background attributes
|
|
pNewCell->SetData(pNewData);
|
|
}
|
|
|
|
// Insert new cells in following rows
|
|
for( int iRow = 0; iRow < iNewRows; iRow++ )
|
|
{
|
|
// Get the next existing row
|
|
if( pRow )
|
|
pRow = (CEditTableRowElement*)pRow->GetNextSibling();
|
|
|
|
if( pRow )
|
|
{
|
|
// Starting with first cell in row, find the cell
|
|
// just after "this" cell's column
|
|
CEditTableCellElement *pCell = (CEditTableCellElement*)pRow->GetChild();
|
|
while( pCell && pCell->GetX() <= m_X )
|
|
pCell = (CEditTableCellElement*)pCell->GetNextSibling();
|
|
|
|
// Insert new cells before the one we found
|
|
for( int i = 0; i < m_iColSpan; i++ )
|
|
{
|
|
pNewCell = new CEditTableCellElement;
|
|
if( pCell )
|
|
pNewCell->InsertBefore(pCell);
|
|
else
|
|
// Row has no cells! (Table is messed up)
|
|
pNewCell->InsertAsFirstChild(pRow);
|
|
|
|
pNewCell->FinishedLoad(pBuffer);
|
|
// Set background attributes
|
|
pNewCell->SetData(pNewData);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If no row, table is messed up.
|
|
// But we can fix it by simply inserting a new row
|
|
CEditTableRowElement *pNewRow = new CEditTableRowElement(m_iColSpan);
|
|
if( pNewRow == NULL )
|
|
break;
|
|
pNewRow->InsertAfter(pCurrentRow);
|
|
pCurrentRow = pNewRow;
|
|
pRow->FinishedLoad(pBuffer);
|
|
// Set background attributes in cells created with the row
|
|
CEditTableCellElement *pNewRowCell = (CEditTableCellElement*)pRow->GetChild();
|
|
while( pNewRowCell )
|
|
{
|
|
pNewRowCell->SetData(pNewData);
|
|
pNewRowCell = (CEditTableCellElement*)pNewRowCell->GetNextSibling();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now reset values in current cell
|
|
pData->iColSpan = 1;
|
|
pData->iRowSpan = 1;
|
|
SetData(pData);
|
|
EDT_FreeTableCellData(pData);
|
|
EDT_FreeTableCellData(pNewData);
|
|
|
|
pBuffer->ClearFillNewCellWithSpace();
|
|
}
|
|
|
|
// Move all contents of supplied cell into this cell
|
|
void CEditTableCellElement::MergeCells(CEditTableCellElement* pCell, int32& iNewRowSpan)
|
|
{
|
|
if( !pCell || pCell == this )
|
|
return;
|
|
|
|
// Be sure cell is not selected
|
|
// We do this in cell's destructor when we delete it below,
|
|
// but its contents will be moved so the wrong LO_CellStruct will
|
|
// be found if we don't do this now.
|
|
GetEditBuffer()->SelectCell(FALSE, NULL, pCell);
|
|
|
|
// Don't merge cells with only single, empty string as only element
|
|
// Just delete the cell to be merged
|
|
if( !pCell->IsEmpty() )
|
|
{
|
|
CEditElement *pLastChild = GetChild();
|
|
if(!pLastChild)
|
|
return;
|
|
|
|
// First child container to move
|
|
CEditElement *pMoveChild = pCell->GetChild();
|
|
|
|
if( IsEmpty() )
|
|
{
|
|
// We are moving into an empty cell,
|
|
// so delete the single container
|
|
delete pLastChild;
|
|
// Make the first element the first
|
|
// container we are moving
|
|
pMoveChild->SetParent( this );
|
|
SetChild(pMoveChild);
|
|
|
|
// Setup for moving other containers
|
|
pLastChild = pMoveChild;
|
|
pMoveChild = pMoveChild->GetNextSibling();
|
|
} else {
|
|
// Insert after last existing child of cell,
|
|
// There is often only 1 container under each cell,
|
|
// except if there are multiple paragraphs or
|
|
// cell contents already merged
|
|
while( pLastChild->GetNextSibling() )
|
|
pLastChild = pLastChild->GetNextSibling();
|
|
}
|
|
|
|
while(pMoveChild)
|
|
{
|
|
// Move container by changing appropriate pointers
|
|
pLastChild->SetNextSibling( pMoveChild );
|
|
pMoveChild->SetParent( this );
|
|
pLastChild = pMoveChild;
|
|
pMoveChild = pMoveChild->GetNextSibling();
|
|
}
|
|
|
|
// Clear pointer to children just moved
|
|
pCell->SetChild(0);
|
|
}
|
|
CEditElement *pNext = pCell->GetNextSibling();
|
|
CEditElement *pPrev = pCell->GetPreviousSibling();
|
|
if( pCell->GetNextSibling() == NULL && pCell->GetPreviousSibling() == NULL )
|
|
{
|
|
// pCell is the only cell in this row, so delete the row
|
|
delete pCell->GetParent();
|
|
// Because we removed a row, the ROWSPAN is now 1 less
|
|
iNewRowSpan--;
|
|
}
|
|
else
|
|
{
|
|
delete pCell;
|
|
}
|
|
}
|
|
|
|
void CEditTableCellElement::DeleteContents(XP_Bool bMarkAsDeleted)
|
|
{
|
|
// Get the first conainer in the cell
|
|
CEditElement *pChild = GetChild();
|
|
|
|
// Delete all other containers
|
|
while( pChild )
|
|
{
|
|
CEditElement *pNext = pChild->GetNextSibling();
|
|
pChild->Unlink();
|
|
delete pChild;
|
|
pChild = pNext;
|
|
}
|
|
// Create a default paragraph container,
|
|
// this will set cell as its parent
|
|
CEditContainerElement *pContainer = CEditContainerElement::NewDefaultContainer(this, ED_ALIGN_DEFAULT);
|
|
|
|
// Initialize the cell contents
|
|
if( pContainer )
|
|
pContainer->FinishedLoad(GetEditBuffer());
|
|
|
|
// Mark this cell as deleted so CEditBuffer::DeleteSelectedCells()
|
|
// can skip over this during multiple deletion passes
|
|
if( bMarkAsDeleted )
|
|
m_bDeleted = TRUE;
|
|
}
|
|
|
|
// Insert as last child of supplied parent
|
|
// but first save current parent and next pointers
|
|
void CEditTableCellElement::SwitchLinkage(CEditTableRowElement *pParentRow)
|
|
{
|
|
// Save current location in tree
|
|
m_pSaveParent = GetParent();
|
|
m_pSaveNext = GetNextSibling();
|
|
#ifdef DEBUG
|
|
// This prevents XP_ASSERT when trying to InsertAsLastChild;
|
|
SetParent(0);
|
|
#endif
|
|
// Insert into new row
|
|
InsertAsLastChild((CEditElement*)pParentRow);
|
|
}
|
|
|
|
// Switches back to saved parent and next pointers
|
|
void CEditTableCellElement::RestoreLinkage()
|
|
{
|
|
// Important! Can't call our CEditTableCellElement::Unlink()
|
|
// because it will remove cell from selection list
|
|
// and the main purpose for using this is to
|
|
// restore selected cells temporarily moved
|
|
// for copying to clipboard
|
|
CEditElement::Unlink();
|
|
SetParent(m_pSaveParent);
|
|
SetNextSibling(m_pSaveNext);
|
|
CEditElement *pParent = GetParent();
|
|
}
|
|
|
|
// Platform-specific end-of-line character(s) added to string,
|
|
// returning in possibly-reallocated buffer that caller must free
|
|
char *edt_AppendEndOfLine(char *pText)
|
|
{
|
|
//TODO: FIX THIS FOR MAC AND UNIX LINE-END CONVENTIONS
|
|
if( pText )
|
|
return PR_sprintf_append( pText, "\r\n" );
|
|
|
|
return NULL;
|
|
}
|
|
|
|
char* CEditTableCellElement::GetText(XP_Bool bJoinParagraphs)
|
|
{
|
|
char *pCellText = NULL;
|
|
CEditElement *pChild = GetChild();
|
|
|
|
//Prevents adding delimiter before first element
|
|
XP_Bool bFirst = TRUE;
|
|
|
|
while( pChild )
|
|
{
|
|
if( pChild->IsContainer() )
|
|
{
|
|
char *pText = pChild->Container()->GetText();
|
|
if( pText && *pText )
|
|
{
|
|
if( !bFirst )
|
|
{
|
|
if( bJoinParagraphs )
|
|
{
|
|
// Add 2 spaces between paragraphs instead of CR/LF
|
|
// so we can merge all contents in table cell
|
|
pCellText = PR_sprintf_append( pCellText, " " );
|
|
} else {
|
|
pCellText = edt_AppendEndOfLine( pCellText);
|
|
}
|
|
}
|
|
pCellText = PR_sprintf_append(pCellText, pText);
|
|
XP_FREE(pText);
|
|
|
|
bFirst = FALSE;
|
|
}
|
|
}
|
|
pChild = pChild->GetNextSibling();
|
|
}
|
|
// We must follow the rule that every cell must have
|
|
// at least 1 empty text element in it,
|
|
// so return an empty string if nothing found
|
|
if( !pCellText )
|
|
return XP_STRDUP("");
|
|
|
|
return pCellText;
|
|
}
|
|
|
|
|
|
// class CEditLayerElement
|
|
|
|
CEditLayerElement::CEditLayerElement()
|
|
: CEditElement(0, P_META) // P_LAYER, 0)
|
|
{
|
|
EDT_LayerData* pData = NewData();
|
|
// pData->iColumns = columns;
|
|
SetData(pData);
|
|
FreeData(pData);
|
|
}
|
|
|
|
CEditLayerElement::CEditLayerElement(CEditElement *pParent, PA_Tag *pTag, int16 /*csid*/)
|
|
: CEditElement(pParent, P_META) // P_LAYER)
|
|
{
|
|
if( pTag ){
|
|
char *locked_buff;
|
|
|
|
PA_LOCK(locked_buff, char *, pTag->data );
|
|
if( locked_buff ){
|
|
SetTagData( locked_buff );
|
|
}
|
|
PA_UNLOCK(pTag->data);
|
|
}
|
|
}
|
|
|
|
CEditLayerElement::CEditLayerElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer)
|
|
: CEditElement(pStreamIn, pBuffer)
|
|
{
|
|
}
|
|
|
|
void CEditLayerElement::FinishedLoad( CEditBuffer* pBuffer ){
|
|
EnsureSelectableSiblings(pBuffer);
|
|
CEditElement::FinishedLoad(pBuffer);
|
|
}
|
|
|
|
void CEditLayerElement::SetData( EDT_LayerData *pData ){
|
|
char *pNew = 0;
|
|
/* if ( pData->iColumns > 1){
|
|
pNew = PR_sprintf_append( pNew, "COLS=%d ", pData->iColumns );
|
|
} */
|
|
if( pData->pExtra ){
|
|
pNew = PR_sprintf_append( pNew, "%s ", pData->pExtra );
|
|
}
|
|
if( pNew ){
|
|
pNew = PR_sprintf_append( pNew, ">" );
|
|
}
|
|
SetTagData( pNew );
|
|
if ( pNew ) {
|
|
free(pNew);
|
|
}
|
|
}
|
|
|
|
EDT_LayerData* CEditLayerElement::GetData( ){
|
|
EDT_LayerData *pRet;
|
|
PA_Tag* pTag = TagOpen(0);
|
|
pRet = ParseParams( pTag, GetWinCSID() );
|
|
PA_FreeTag( pTag );
|
|
return pRet;
|
|
}
|
|
|
|
static char *LayerParams[] = {
|
|
// PARAM_COLS,
|
|
0
|
|
};
|
|
|
|
EDT_LayerData* CEditLayerElement::ParseParams( PA_Tag *pTag, int16 csid ){
|
|
EDT_LayerData *pData = NewData();
|
|
|
|
// pData->iColumns = edt_FetchParamInt(pTag, PARAM_COLS, 1);
|
|
pData->pExtra = edt_FetchParamExtras( pTag, LayerParams, csid );
|
|
|
|
return pData;
|
|
}
|
|
|
|
EDT_LayerData* CEditLayerElement::NewData(){
|
|
EDT_LayerData *pData = XP_NEW( EDT_LayerData );
|
|
if( pData == 0 ){
|
|
// throw();
|
|
return pData;
|
|
}
|
|
// pData->iColumns = 1;
|
|
pData->pExtra = 0;
|
|
return pData;
|
|
}
|
|
|
|
void CEditLayerElement::FreeData( EDT_LayerData *pData ){
|
|
if( pData->pExtra ) XP_FREE( pData->pExtra );
|
|
XP_FREE( pData );
|
|
}
|
|
|
|
void CEditLayerElement::PrintOpen( CPrintState *pPrintState ){
|
|
TagType tSave = GetType();
|
|
SetType( P_LAYER );
|
|
CEditElement::PrintOpen( pPrintState );
|
|
SetType( tSave );
|
|
}
|
|
|
|
void CEditLayerElement::PrintEnd( CPrintState *pPrintState ){
|
|
TagType tSave = GetType();
|
|
SetType( P_LAYER );
|
|
CEditElement::PrintEnd( pPrintState );
|
|
SetType( tSave );
|
|
}
|
|
|
|
// CEditDivisionElement
|
|
|
|
CEditDivisionElement::CEditDivisionElement()
|
|
: CEditElement(0, P_DIVISION, 0)
|
|
{
|
|
EDT_DivisionData* pData = NewData();
|
|
SetData(pData);
|
|
FreeData(pData);
|
|
}
|
|
|
|
CEditDivisionElement::CEditDivisionElement(CEditElement *pParent, PA_Tag *pTag, int16 /*csid*/)
|
|
: CEditElement(pParent, P_DIVISION)
|
|
{
|
|
if( pTag ){
|
|
char *locked_buff;
|
|
|
|
PA_LOCK(locked_buff, char *, pTag->data );
|
|
if( locked_buff ){
|
|
SetTagData( locked_buff );
|
|
}
|
|
PA_UNLOCK(pTag->data);
|
|
}
|
|
}
|
|
|
|
CEditDivisionElement::CEditDivisionElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer)
|
|
: CEditElement(pStreamIn, pBuffer)
|
|
{
|
|
}
|
|
|
|
XP_Bool CEditDivisionElement::IsDivision() { return TRUE; }
|
|
CEditDivisionElement* CEditDivisionElement::Cast(CEditElement* pElement) {
|
|
return pElement && pElement->IsDivision() ? (CEditDivisionElement*) pElement : 0; }
|
|
|
|
EEditElementType CEditDivisionElement::GetElementType() { return eDivisionElement; }
|
|
XP_Bool CEditDivisionElement::IsAcceptableChild(CEditElement& pChild){
|
|
return ! pChild.IsLeaf();
|
|
}
|
|
|
|
void CEditDivisionElement::SetData( EDT_DivisionData *pData ){
|
|
char *pNew = 0;
|
|
if( pData->align != ED_ALIGN_DEFAULT ){
|
|
pNew = PR_sprintf_append( pNew, "ALIGN=%s ", EDT_AlignString(pData->align) );
|
|
}
|
|
if( pData->pExtra ){
|
|
pNew = PR_sprintf_append( pNew, "%s ", pData->pExtra );
|
|
}
|
|
if( pNew ){
|
|
pNew = PR_sprintf_append( pNew, ">" );
|
|
}
|
|
SetTagData( pNew );
|
|
if ( pNew ) {
|
|
free(pNew);
|
|
}
|
|
}
|
|
|
|
EDT_DivisionData* CEditDivisionElement::GetData( ){
|
|
EDT_DivisionData *pRet;
|
|
PA_Tag* pTag = TagOpen(0);
|
|
pRet = ParseParams( pTag, GetWinCSID() );
|
|
PA_FreeTag( pTag );
|
|
return pRet;
|
|
}
|
|
|
|
ED_Alignment CEditDivisionElement::GetAlignment(){
|
|
ED_Alignment result = ED_ALIGN_DEFAULT;
|
|
EDT_DivisionData* pData = GetData();
|
|
if ( pData ) {
|
|
result = pData->align;
|
|
FreeData(pData);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void CEditDivisionElement::ClearAlignment(){
|
|
EDT_DivisionData* pData = GetData();
|
|
if ( pData ) {
|
|
pData->align = ED_ALIGN_DEFAULT;
|
|
SetData(pData);
|
|
FreeData(pData);
|
|
}
|
|
}
|
|
|
|
XP_Bool CEditDivisionElement::HasData(){
|
|
// Does this have data other than the ALIGN tag?
|
|
XP_Bool hasData = FALSE;
|
|
EDT_DivisionData* pData = GetData();
|
|
if ( pData ) {
|
|
if ( pData->pExtra || pData->align != ED_ALIGN_DEFAULT ) {
|
|
hasData = TRUE;
|
|
}
|
|
FreeData(pData);
|
|
}
|
|
return hasData;
|
|
}
|
|
|
|
static char *DivisionParams[] = {
|
|
PARAM_ALIGN,
|
|
0
|
|
};
|
|
|
|
EDT_DivisionData* CEditDivisionElement::ParseParams( PA_Tag *pTag, int16 csid ){
|
|
EDT_DivisionData *pData = NewData();
|
|
|
|
pData->pExtra = edt_FetchParamExtras( pTag, DivisionParams, csid );
|
|
pData->align = edt_FetchParamAlignment( pTag, ED_ALIGN_DEFAULT, FALSE, csid );
|
|
|
|
return pData;
|
|
}
|
|
|
|
EDT_DivisionData* CEditDivisionElement::NewData(){
|
|
EDT_DivisionData *pData = XP_NEW( EDT_DivisionData );
|
|
if( pData == 0 ){
|
|
// throw();
|
|
return pData;
|
|
}
|
|
pData->align = ED_ALIGN_DEFAULT;
|
|
pData->pExtra = 0;
|
|
return pData;
|
|
}
|
|
|
|
void CEditDivisionElement::FreeData( EDT_DivisionData *pData ){
|
|
if ( pData ) {
|
|
XP_FREEIF( pData->pExtra );
|
|
XP_FREE( pData );
|
|
}
|
|
}
|
|
|
|
void CEditDivisionElement::PrintOpen( CPrintState *pPrintState ){
|
|
// Don't print <DIV> tag if it is a no-op.
|
|
if ( HasData() ) {
|
|
CEditElement::PrintOpen(pPrintState);
|
|
}
|
|
}
|
|
|
|
void CEditDivisionElement::PrintEnd( CPrintState *pPrintState ){
|
|
// Don't print <DIV> tag if it is a no-op.
|
|
if ( HasData() ) {
|
|
CEditElement::PrintEnd(pPrintState);
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CEditRootDocElement
|
|
//-----------------------------------------------------------------------------
|
|
void CEditRootDocElement::PrintOpen( CPrintState *pPrintState ){
|
|
m_pBuffer->PrintDocumentHead( pPrintState );
|
|
}
|
|
|
|
void CEditRootDocElement::PrintEnd( CPrintState *pPrintState ){
|
|
m_pBuffer->PrintDocumentEnd( pPrintState );
|
|
}
|
|
|
|
void CEditRootDocElement::FinishedLoad( CEditBuffer *pBuffer ){
|
|
CEditSubDocElement::FinishedLoad(pBuffer);
|
|
if ( ! GetLastChild() || ! GetLastChild()->IsEndContainer() ) {
|
|
CEditEndContainerElement* pEndContainer = new CEditEndContainerElement((CEditElement *)NULL);
|
|
// Creating the element automaticly inserts it.
|
|
(void) new CEditEndElement(pEndContainer);
|
|
pEndContainer->InsertAsLastChild(this);
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void CEditRootDocElement::ValidateTree(){
|
|
CEditElement::ValidateTree();
|
|
XP_ASSERT(GetParent() == NULL);
|
|
XP_ASSERT(GetChild() != NULL);
|
|
XP_ASSERT(GetLastMostChild()->GetElementType() == eEndElement);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CEditLeafElement
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
XP_Bool CEditLeafElement::PrevPosition(ElementOffset iOffset,
|
|
CEditLeafElement*& pNew, ElementOffset& iNewOffset ){
|
|
|
|
XP_Bool bPreviousElement = FALSE;
|
|
XP_Bool bMoved = TRUE;
|
|
|
|
iNewOffset = iOffset - 1;
|
|
pNew = this;
|
|
|
|
if( iNewOffset == 0 ){
|
|
|
|
CEditElement *pPrev = PreviousLeafInContainer();
|
|
if( pPrev ){
|
|
if( pPrev->IsLeaf()){
|
|
bPreviousElement = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// we need to set the position to the end of the previous element.
|
|
//
|
|
if( iNewOffset < 0 || bPreviousElement ){
|
|
CEditLeafElement *pPrev = PreviousLeaf();
|
|
if( pPrev ){
|
|
if( pPrev->IsLeaf()){
|
|
pNew = pPrev;
|
|
iNewOffset = pNew->Leaf()->GetLen();
|
|
}
|
|
}
|
|
else{
|
|
// no previous element, we are at the beginning of the buffer.
|
|
iNewOffset = iOffset;
|
|
bMoved = FALSE;
|
|
}
|
|
}
|
|
return bMoved;
|
|
}
|
|
|
|
XP_Bool CEditLeafElement::NextPosition(ElementOffset iOffset,
|
|
CEditLeafElement*& pNew, ElementOffset& iNewOffset ){
|
|
LO_Element* pRetText;
|
|
CEditLeafElement *pNext;
|
|
int iRetPos;
|
|
XP_Bool retVal = TRUE;
|
|
|
|
pNew = this;
|
|
iNewOffset = iOffset + 1;
|
|
|
|
//
|
|
// if the new offset is obviously greater, move past it. If
|
|
// Make sure the layout engine actually layed out the element.
|
|
//
|
|
if( iNewOffset > Leaf()->GetLen() ||
|
|
!Leaf()->GetLOElementAndOffset( iOffset, FALSE, pRetText, iRetPos ))
|
|
{
|
|
pNext = NextLeaf();
|
|
if( pNext ){
|
|
pNew = pNext;
|
|
if( pNext->PreviousLeafInContainer() == 0 ){
|
|
iNewOffset = 0;
|
|
}
|
|
else {
|
|
iNewOffset = 1;
|
|
}
|
|
}
|
|
else {
|
|
iNewOffset = iOffset;
|
|
retVal = FALSE;
|
|
}
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
void CEditLeafElement::DisconnectLayoutElements(LO_Element* /*pElement*/){
|
|
// The intention of this code was to make sure that live layout elements
|
|
// didn't have back-pointers to dead edit elements.
|
|
//
|
|
// Unfortunately, if an edit element is cut out of the root,
|
|
// and then deleted after a relayout,
|
|
// the layout element will be stale. And that will cause
|
|
// a Free Memory Read. This isn't usually a problem, except
|
|
// on NT, where the rolling heap may have already freed the
|
|
// heap that had the stale layout element. This happens
|
|
// most frequently when large tables are deleted.
|
|
//
|
|
// This code doesn't seem to be needed for correct operation of
|
|
// Composer. That's a good thing, because it can't be
|
|
// made to work without a formal set of rules on the
|
|
// lifetime of edit elements and layout elements.
|
|
#if 0
|
|
// Clear back pointer out of the edit elements.
|
|
LO_Element* e = pElement;
|
|
while ( e && e->lo_any.edit_element == this ){
|
|
e->lo_any.edit_element = 0;
|
|
while ( e && e->lo_any.edit_element == 0 ){
|
|
e = e->lo_any.next;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CEditLeafElement::SetLayoutElementHelper( intn desiredType, LO_Element** pElementHolder,
|
|
intn iEditOffset, intn lo_type,
|
|
LO_Element* pLoElement){
|
|
// XP_TRACE(("SetLayoutElementHelper this(0x%08x) iEditOffset(%d) pLoElement(0x%08x)", this, iEditOffset, pLoElement));
|
|
|
|
if( lo_type == desiredType ){
|
|
// iEditOffset is:
|
|
// 0 for normal case,
|
|
// -1 for end of document element.
|
|
// > 0 for text items after a line wrap.
|
|
// Don't disconnect layout elements because this is
|
|
// called for each layout element of a wrapped text
|
|
// edit element. Since DisconnectLayoutElements goes
|
|
// ahead and zeros out all the "next" elements with
|
|
// the same back pointer, we end up trashing later
|
|
// elements if the elements are set in some order
|
|
// other than first-to-last. This happens when just the 2nd line of a two-line
|
|
// text element is being relaid out.
|
|
if ( iEditOffset <= 0 ) {
|
|
// Don't reset the old element, because functions like lo_BreakOldElement depend upon the
|
|
// old element having a good value.
|
|
*pElementHolder = pLoElement;
|
|
}
|
|
pLoElement->lo_any.edit_element = this;
|
|
pLoElement->lo_any.edit_offset = iEditOffset;
|
|
}
|
|
else {
|
|
// We get called back whenever linefeeds are generated. That's so that
|
|
// breaks can find their linefeed. But it also means we are
|
|
// called back more often than nescessary.
|
|
// XP_TRACE((" ignoring inconsistent type.\n"));
|
|
}
|
|
}
|
|
|
|
void CEditLeafElement::ResetLayoutElementHelper( LO_Element** pElementHolder, intn iEditOffset,
|
|
LO_Element* pLoElement ){
|
|
// XP_TRACE(("ResetLayoutElementHelper this(0x%08x) iEditOffset(%d) pLoElement(0x%08x)", this, iEditOffset, pLoElement));
|
|
if ( iEditOffset <= 0 ) {
|
|
if ( *pElementHolder == pLoElement ){
|
|
*pElementHolder = NULL;
|
|
}
|
|
else {
|
|
XP_TRACE((" Woops -- we're currently pointing to 0x%08x\n", *pElementHolder));
|
|
}
|
|
}
|
|
}
|
|
|
|
CEditElement* CEditLeafElement::Divide(int iOffset){
|
|
CEditElement* pNext;
|
|
if( iOffset == 0 ){
|
|
return this;
|
|
}
|
|
|
|
pNext = LeafInContainerAfter();
|
|
if( iOffset >= GetLen() ){
|
|
if( pNext != 0 ){
|
|
return pNext;
|
|
}
|
|
else {
|
|
CEditTextElement* pPrev;
|
|
CEditTextElement* pNew;
|
|
if( IsA( P_TEXT )) {
|
|
pPrev = this->Text();
|
|
}
|
|
else {
|
|
pPrev = PreviousTextInContainer();
|
|
}
|
|
|
|
if( pPrev ){
|
|
pNew = pPrev->CopyEmptyText();
|
|
}
|
|
else {
|
|
pNew = new CEditTextElement((CEditElement*)0,0);
|
|
}
|
|
pNew->InsertAfter(this);
|
|
return pNew;
|
|
}
|
|
}
|
|
|
|
// offset is in the middle somewhere Must be a TEXT.
|
|
XP_ASSERT( IsA(P_TEXT) );
|
|
CEditTextElement* pNew = Text()->CopyEmptyText();
|
|
pNew->SetText( Text()->GetText()+iOffset );
|
|
Text()->GetText()[iOffset] = 0;
|
|
|
|
/* update the text block with the new text */
|
|
CEditTextElement * pText = Text();
|
|
if ( pText->m_pTextBlock != NULL ){
|
|
lo_ChangeText ( pText->m_pTextBlock, pText->GetTextWithConvertedSpaces() );
|
|
}
|
|
|
|
pNew->InsertAfter(this);
|
|
return pNew;
|
|
}
|
|
|
|
CEditTextElement* CEditLeafElement::CopyEmptyText(CEditElement *pParent){
|
|
return new CEditTextElement(pParent,0);
|
|
}
|
|
|
|
CPersistentEditInsertPoint CEditLeafElement::GetPersistentInsertPoint(ElementOffset offset){
|
|
ElementIndex len = GetPersistentCount();
|
|
if ( offset > len )
|
|
{
|
|
#ifdef DEBUG_EDIT_LAYOUT
|
|
XP_ASSERT(FALSE);
|
|
#endif
|
|
offset = len;
|
|
}
|
|
return CPersistentEditInsertPoint(GetElementIndex() + offset, offset == 0);
|
|
}
|
|
|
|
XP_Bool CEditLeafElement::IsUnknownHTML(){
|
|
return FALSE;
|
|
}
|
|
|
|
XP_Bool CEditLeafElement::IsComment(){
|
|
return FALSE;
|
|
}
|
|
|
|
XP_Bool CEditLeafElement::IsComment(char* /*prefix*/){
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Default implementation
|
|
//
|
|
PA_Tag* CEditLeafElement::TagOpen( int /* iEditOffset */ ){
|
|
PA_Tag *pTag = XP_NEW( PA_Tag );
|
|
XP_BZERO( pTag, sizeof( PA_Tag ) );
|
|
if( GetTagData() ){
|
|
SetTagData( pTag, GetTagData() );
|
|
}
|
|
else {
|
|
SetTagData( pTag, ">" );
|
|
}
|
|
return pTag;
|
|
}
|
|
|
|
ElementIndex CEditLeafElement::GetPersistentCount()
|
|
{
|
|
return GetLen();
|
|
}
|
|
|
|
void CEditLeafElement::GetAll(CEditSelection& selection) {
|
|
selection.m_start.m_pElement = this;
|
|
selection.m_start.m_iPos = 0;
|
|
selection.m_end.m_pElement = this;
|
|
selection.m_end.m_iPos = Leaf()->GetLen();
|
|
selection.m_bFromStart = FALSE;
|
|
}
|
|
|
|
XP_Bool CEditLeafElement::IsAcceptableChild(CEditElement& /*pChild*/ ) {return FALSE;}
|
|
XP_Bool CEditLeafElement::IsContainerContainer() { return FALSE; }
|
|
|
|
/* Optimized version for leaves. */
|
|
|
|
XP_Bool CEditLeafElement::IsFirstInContainer(){
|
|
CEditContainerElement* pContainer = FindContainer();
|
|
if ( ! pContainer ) {
|
|
return TRUE;
|
|
}
|
|
CEditElement* pOther = pContainer->GetChild();
|
|
return pOther == this;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
void CEditLeafElement::ValidateTree(){
|
|
CEditElement::ValidateTree();
|
|
CEditElement *pChild = GetChild();
|
|
XP_ASSERT(!pChild); // Must have no children.
|
|
// Parent must exist and be a container
|
|
CEditElement *pParent = GetParent();
|
|
XP_ASSERT(pParent);
|
|
XP_ASSERT(pParent->IsContainer());
|
|
}
|
|
#endif
|
|
|
|
XP_Bool CEditLeafElement::IsMisspelled() {
|
|
return (IsText() && Text()->m_tf & TF_SPELL);
|
|
}
|
|
|
|
|
|
void CEditLeafElement::StreamToPositionalText( IStreamOut *pOut, XP_Bool bEnd ){
|
|
if( !bEnd ){
|
|
XP_ASSERT( GetLen() == 1 );
|
|
pOut->Write(" ",1);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CEditContainerElement
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Static constructor to create default element
|
|
//
|
|
CEditContainerElement* CEditContainerElement::NewDefaultContainer( CEditElement *pParent,
|
|
ED_Alignment align ){
|
|
PA_Tag *pTag = XP_NEW( PA_Tag );
|
|
XP_BZERO( pTag, sizeof( PA_Tag ) );
|
|
#ifdef EDT_DDT
|
|
pTag->type = P_NSDT;
|
|
#else
|
|
pTag->type = P_DESC_TITLE;
|
|
#endif
|
|
// CSID doesn't matter because there's no data in the pTag, so there's no i18n issues.
|
|
CEditContainerElement *pRet = new CEditContainerElement( pParent, pTag, 0, align );
|
|
PA_FreeTag( pTag );
|
|
return pRet;
|
|
}
|
|
|
|
CEditContainerElement::CEditContainerElement(CEditElement *pParent, PA_Tag *pTag, int16 csid,
|
|
ED_Alignment defaultAlign ):
|
|
CEditElement(pParent, pTag ? pTag->type : P_PARAGRAPH),
|
|
m_defaultAlign( defaultAlign ),
|
|
m_align( defaultAlign ),
|
|
m_hackPreformat(0),
|
|
m_bHasEndTag(FALSE)
|
|
{
|
|
|
|
if( pTag ){
|
|
EDT_ContainerData *pData = ParseParams( pTag, csid );
|
|
SetData( pData );
|
|
FreeData( pData );
|
|
}
|
|
}
|
|
|
|
CEditContainerElement::CEditContainerElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer):
|
|
CEditElement( pStreamIn, pBuffer )
|
|
{
|
|
m_defaultAlign = (ED_Alignment) pStreamIn->ReadInt();
|
|
m_align = (ED_Alignment) pStreamIn->ReadInt();
|
|
m_bHasEndTag = FALSE;
|
|
}
|
|
|
|
void CEditContainerElement::StreamOut( IStreamOut *pOut ){
|
|
CEditElement::StreamOut( pOut );
|
|
pOut->WriteInt( m_defaultAlign );
|
|
pOut->WriteInt( m_align );
|
|
}
|
|
|
|
void CEditContainerElement::StreamInChildren(IStreamIn* pIn, CEditBuffer* pBuffer){
|
|
CEditElement::StreamInChildren(pIn, pBuffer);
|
|
if ( GetChild() == NULL ){
|
|
// I need a dummy child.
|
|
// g++ gets issues a spurious warning about the value not being used.
|
|
// Creating it automaticly inserts it
|
|
(void) new CEditTextElement(this, 0);
|
|
}
|
|
}
|
|
|
|
ElementIndex CEditContainerElement::GetPersistentCount(){
|
|
// One extra element: the end of the last item in the container.
|
|
return CEditElement::GetPersistentCount() + 1;
|
|
}
|
|
|
|
|
|
|
|
void CEditContainerElement::FinishedLoad( CEditBuffer *pBuffer )
|
|
{
|
|
if( !pBuffer )
|
|
pBuffer = GetEditBuffer();
|
|
if( !pBuffer )
|
|
{
|
|
XP_ASSERT(FALSE);
|
|
CEditElement::FinishedLoad(pBuffer);
|
|
return;
|
|
}
|
|
|
|
XP_Bool bFillNewCellWithSpace = FALSE;
|
|
|
|
if ( GetType() != P_PREFORMAT )
|
|
{
|
|
// Don't allow more than one space before non-text elements
|
|
CEditElement* pNext;
|
|
CEditElement* pParent = GetParent();
|
|
CEditElement *pChild = GetChild();
|
|
|
|
// Detect the case of a single container with a single
|
|
// text element with just a space in a table cell
|
|
// This is OK and is needed to show cell border in browser
|
|
XP_Bool bIsTableCellWithOneSpace = FALSE;
|
|
if( pParent && pParent->IsTableCell() && GetNextSibling() == NULL )
|
|
{
|
|
if( !pChild )
|
|
// If creating a new cell and preference is set,
|
|
// the empty cell should have a space,
|
|
// so delete this and a new one will be created below
|
|
//bFillNewCellWithSpace = pBuffer ? pBuffer->FillNewCellWithSpace() : FALSE;
|
|
bFillNewCellWithSpace = pBuffer->FillNewCellWithSpace();
|
|
|
|
if( pChild && pChild->IsText() &&
|
|
(pChild->GetNextSibling() == NULL ||
|
|
pChild->GetNextSibling()->GetType() == P_UNKNOWN) )
|
|
{
|
|
// We have a single container in a table cell with a single text child
|
|
CEditTextElement* pTextChild = pChild->Text();
|
|
char* pText = pTextChild->GetText();
|
|
|
|
bFillNewCellWithSpace = pBuffer->FillNewCellWithSpace();
|
|
|
|
if( pTextChild->GetLen() == 0 && bFillNewCellWithSpace )
|
|
{
|
|
delete pChild;
|
|
}
|
|
else if( pTextChild->GetLen() == 1 && pText[0] == ' ')
|
|
{
|
|
// We have a single space, so don't do special trimming below
|
|
bIsTableCellWithOneSpace = TRUE;
|
|
}
|
|
}
|
|
CEditBuffer *t_buffer;
|
|
t_buffer = GetEditBuffer();
|
|
if( ! bIsTableCellWithOneSpace && (t_buffer && !t_buffer->m_bNoRelayout))
|
|
{
|
|
for ( CEditElement* pChild = GetChild();
|
|
pChild;
|
|
pChild = pNext )
|
|
{
|
|
pNext = pChild->GetNextSibling();
|
|
if ( pChild->IsText() && ! (pNext && pNext->IsText()) )
|
|
{
|
|
CEditTextElement* pTextChild = pChild->Text();
|
|
char* pText = pTextChild->GetText();
|
|
int size = pTextChild->GetLen();
|
|
XP_Bool trimming = FALSE;
|
|
//do not allow 1 byte of space to be a child
|
|
if (size == 1 && GetType() == P_NSDT && pText[0] == ' ')
|
|
{
|
|
trimming = TRUE;
|
|
size--;
|
|
}
|
|
|
|
while ( size >0 && pText[size-1] == '\n' ||
|
|
(size > 1 && pText[size-1] == ' ' && pText[size-2] == ' ') )
|
|
{
|
|
// More than one character of white space at the end of
|
|
// a text element will get laid out as one character.
|
|
trimming = TRUE;
|
|
size--;
|
|
}
|
|
if ( trimming )
|
|
{
|
|
if (size <= 0 )
|
|
{
|
|
delete pChild;
|
|
}
|
|
else
|
|
{
|
|
char* pCopy = XP_STRDUP(pText);
|
|
pCopy[size] = '\0';
|
|
pTextChild->SetText(pCopy);
|
|
XP_FREE(pCopy);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( GetChild() == NULL )
|
|
{
|
|
// I need a dummy child -- might be a space for new cell
|
|
CEditTextElement* pChild = new CEditTextElement(this, bFillNewCellWithSpace ? " " : 0);
|
|
// Creating it automaticly inserts it
|
|
pChild->FinishedLoad(pBuffer);
|
|
}
|
|
//
|
|
// if the last element in a paragraph is a <br>, it is ignored by the browser.
|
|
// So we should trim it.
|
|
// we should do this only once!!!
|
|
CEditElement *pChild;
|
|
CEditElement* pPrev;
|
|
if( (pChild = GetLastChild())!= 0 && pChild->IsBreak())
|
|
{
|
|
pPrev = pChild->GetPreviousSibling();
|
|
if (! (pPrev && pPrev->IsBreak()) )
|
|
delete pChild;
|
|
}
|
|
|
|
CEditElement::FinishedLoad(pBuffer);
|
|
}
|
|
|
|
|
|
|
|
XP_Bool CEditContainerElement::ForcesDoubleSpacedNextLine() {
|
|
return BitSet( edt_setContainerHasLineAfter, GetType() );
|
|
}
|
|
|
|
//helper function
|
|
//default == left!
|
|
PRIVATE XP_Bool CompareAlignments(ED_Alignment x, ED_Alignment y)
|
|
{
|
|
if (x == y)
|
|
return TRUE;
|
|
if ( ((x == ED_ALIGN_ABSCENTER) || (x == ED_ALIGN_CENTER))
|
|
&& ((y == ED_ALIGN_ABSCENTER) || (y == ED_ALIGN_CENTER)) )
|
|
return TRUE;
|
|
if ( ((x == ED_ALIGN_DEFAULT) || (x == ED_ALIGN_LEFT))
|
|
&& ((y == ED_ALIGN_DEFAULT) || (y == ED_ALIGN_LEFT)) )
|
|
return TRUE;
|
|
if ( ((x == ED_ALIGN_ABSBOTTOM) || (x == ED_ALIGN_BOTTOM))
|
|
&& ((y == ED_ALIGN_ABSBOTTOM) || (y == ED_ALIGN_BOTTOM)) )
|
|
return TRUE;
|
|
if ( ((x == ED_ALIGN_ABSTOP) || (x == ED_ALIGN_TOP))
|
|
&& ((y == ED_ALIGN_ABSTOP) || (y == ED_ALIGN_TOP)) )
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
void CEditContainerElement::AdjustContainers( CEditBuffer *pBuffer ){
|
|
|
|
if( GetType() == P_PARAGRAPH
|
|
|| (pBuffer->IsComposeWindow()
|
|
&& GetType() == P_PREFORMAT
|
|
&& m_hackPreformat ) ){
|
|
|
|
XP_Bool bInsertNewContainer = FALSE;
|
|
XP_Bool bIsParagraph = GetType() == P_PARAGRAPH;
|
|
|
|
//
|
|
// Convert to internal paragraph type
|
|
//
|
|
SetType( P_NSDT );
|
|
CEditElement *pPreviousSibling = (CEditElement*) GetPreviousSibling();
|
|
CEditContainerElement *pPrev = 0;
|
|
CEditContainerElement *pNext = (CEditContainerElement*) GetNextSibling();
|
|
//
|
|
// Just some paranoia
|
|
//
|
|
if ( pPreviousSibling && pPreviousSibling->IsContainer() )
|
|
pPrev = (CEditContainerElement*) pPreviousSibling;
|
|
|
|
if (!pNext)
|
|
bInsertNewContainer = TRUE;
|
|
else if (pNext->IsContainer())
|
|
{
|
|
//
|
|
// If the alignments of this container and the next one
|
|
// don't match, add a blank container after this container.
|
|
//
|
|
// - Or -
|
|
//
|
|
// If this paragraph has an end tag, and the next container
|
|
// is an NSDT, add a blank container after this paragraph.
|
|
//
|
|
// This will allow us to handle cases like:
|
|
//
|
|
// <P></P><BR>
|
|
// <P></P>text
|
|
// <P></P><IMG>
|
|
//
|
|
|
|
if (!CompareAlignments(GetAlignment(), pNext->GetAlignment()) ||
|
|
(bIsParagraph && m_bHasEndTag && pNext->GetType() == P_NSDT))
|
|
bInsertNewContainer = TRUE;
|
|
}
|
|
else if (bIsParagraph &&
|
|
((!IsListContainer(pNext->GetType()) && !pNext->IsTable()) ||
|
|
(m_bHasEndTag && pNext->IsTable())))
|
|
{
|
|
//
|
|
// Dont' add a blank container between a paragraph and
|
|
// a list ... layout does that for us for free.
|
|
//
|
|
// If the paragraph is followed by a table, add a blank
|
|
// container only if the paragraph has a </P> tag.
|
|
//
|
|
|
|
bInsertNewContainer = TRUE;
|
|
}
|
|
|
|
if ( bInsertNewContainer ){
|
|
CEditContainerElement *pNew;
|
|
pNew = CEditContainerElement::NewDefaultContainer( 0,
|
|
GetAlignment() );
|
|
// g++ thinks the following value is not used, but it's mistaken.
|
|
// The constructor auto-inserts the element into the tree.
|
|
(void) new CEditBreakElement(pNew, 0);
|
|
pNew->InsertAfter( this );
|
|
}
|
|
|
|
//
|
|
// If there is a previous container, append an empty single spaced
|
|
// paragraph before this one. also append an empty single spaced paragraph if it was a table.
|
|
//
|
|
if ((pPreviousSibling && pPreviousSibling->GetType() == P_TABLE) ||
|
|
(pPreviousSibling && pPreviousSibling->GetType() == P_DIVISION) ||
|
|
( pPrev && !pPrev->ForcesDoubleSpacedNextLine()) )
|
|
{
|
|
CEditContainerElement *pNew;
|
|
pNew = CEditContainerElement::NewDefaultContainer( 0,
|
|
GetAlignment() );
|
|
// g++ thinks the following value is not used, but it's mistaken.
|
|
// The constructor auto-inserts the element into the tree.
|
|
(void) new CEditTextElement(pNew, 0);
|
|
pNew->InsertBefore( this );
|
|
}
|
|
}
|
|
|
|
if( BitSet( edt_setContainerBreakConvert, GetType() )) {
|
|
// Find any breaks in this paragraph and convert them into paragraphs
|
|
CEditLeafElement *pChild = (CEditLeafElement*) GetChild();
|
|
CEditLeafElement *pPrev = 0;
|
|
|
|
while( pChild ){
|
|
CEditLeafElement *pNext = pChild->LeafInContainerAfter();
|
|
if( pChild->IsBreak()
|
|
#ifdef USE_SCRIPT
|
|
&& !(pPrev && pPrev->IsText() &&
|
|
(pPrev->Text()->m_tf & (TF_SCRIPT|TF_STYLE|TF_SERVER)) != 0
|
|
)
|
|
&& !(pNext && pNext->IsText() &&
|
|
(pNext->Text()->m_tf & (TF_SCRIPT|TF_STYLE|TF_SERVER)) != 0
|
|
)
|
|
#endif
|
|
){
|
|
//
|
|
// If the first thing in the paragraph is a break,
|
|
// make the paragraph contain empty text
|
|
//
|
|
if( pChild->PreviousLeafInContainer() == 0 ){
|
|
CEditTextElement *pNewText = new CEditTextElement((CEditElement*)0, 0);
|
|
pNewText->InsertBefore( pChild );
|
|
}
|
|
|
|
// Break the chain of children So unlink doesn't try and
|
|
// merge the chain
|
|
pChild->SetNextSibling(0);
|
|
|
|
// Remove the break.
|
|
pChild->Unlink();
|
|
delete pChild;
|
|
|
|
pChild = 0;
|
|
|
|
// reparent the children.
|
|
if( pNext ){
|
|
//
|
|
// Create a new container
|
|
//
|
|
CEditContainerElement *pNew;
|
|
pNew = CEditContainerElement::NewDefaultContainer( 0,
|
|
GetAlignment() );
|
|
pNew->InsertAfter(this);
|
|
|
|
pNew->SetChild(pNext);
|
|
while( pNext ){
|
|
pNext->SetParent( pNew );
|
|
pNext = (CEditLeafElement*)pNext->GetNextSibling();
|
|
}
|
|
}
|
|
}
|
|
pPrev = pChild;
|
|
pChild = pNext;
|
|
}
|
|
}
|
|
|
|
CEditElement::AdjustContainers(pBuffer);
|
|
}
|
|
|
|
PA_Tag* CEditContainerElement::TagOpen( int iEditOffset )
|
|
{
|
|
PA_Tag *pRet = 0;
|
|
PA_Tag* pTag;
|
|
|
|
// create the DIV tag if we need to.
|
|
if( m_align == ED_ALIGN_LEFT || m_align == ED_ALIGN_ABSCENTER || m_align == ED_ALIGN_RIGHT )
|
|
{
|
|
pTag = XP_NEW( PA_Tag );
|
|
XP_BZERO( pTag, sizeof( PA_Tag ) );
|
|
// Explicitely create LEFT <DIV> element if not ED_ALIGN_DEFAULT
|
|
if( m_align== ED_ALIGN_LEFT )
|
|
{
|
|
SetTagData( pTag, "ALIGN=left>");
|
|
pTag->type = P_DIVISION;
|
|
}
|
|
else if( m_align== ED_ALIGN_RIGHT )
|
|
{
|
|
SetTagData( pTag, "ALIGN=right>");
|
|
pTag->type = P_DIVISION;
|
|
}
|
|
else
|
|
{
|
|
SetTagData( pTag, ">");
|
|
pTag->type = P_CENTER;
|
|
}
|
|
pRet = pTag;
|
|
}
|
|
|
|
// create the actual paragraph tag
|
|
if( GetTagData() )
|
|
{
|
|
pTag = XP_NEW( PA_Tag );
|
|
XP_BZERO( pTag, sizeof( PA_Tag ) );
|
|
SetTagData( pTag, GetTagData() );
|
|
}
|
|
else
|
|
{
|
|
pTag = CEditElement::TagOpen( iEditOffset );
|
|
}
|
|
|
|
// link the tags together.
|
|
if( pRet == 0 )
|
|
pRet = pTag;
|
|
else
|
|
pRet->next = pTag;
|
|
|
|
return pRet;
|
|
}
|
|
|
|
PA_Tag* CEditContainerElement::TagEnd( )
|
|
{
|
|
PA_Tag *pRet = CEditElement::TagEnd();
|
|
if( m_align == ED_ALIGN_LEFT || m_align == ED_ALIGN_ABSCENTER || m_align == ED_ALIGN_RIGHT )
|
|
{
|
|
PA_Tag* pTag = XP_NEW( PA_Tag );
|
|
XP_BZERO( pTag, sizeof( PA_Tag ) );
|
|
pTag->is_end = TRUE;
|
|
// Explicitely create LEFT <DIV> element if not ED_ALIGN_DEFAULT
|
|
if( m_align== ED_ALIGN_LEFT )
|
|
{
|
|
pTag->type = P_DIVISION;
|
|
}
|
|
else if( m_align == ED_ALIGN_RIGHT )
|
|
{
|
|
pTag->type = P_DIVISION;
|
|
}
|
|
else {
|
|
pTag->type = P_CENTER;
|
|
}
|
|
|
|
if( pRet == 0 )
|
|
pRet = pTag;
|
|
else
|
|
pRet->next = pTag;
|
|
}
|
|
return pRet;
|
|
}
|
|
|
|
EEditElementType CEditContainerElement::GetElementType()
|
|
{
|
|
return eContainerElement;
|
|
}
|
|
|
|
XP_Bool CEditContainerElement::IsPlainFirstContainer(){
|
|
if ( (GetType() == P_PARAGRAPH || GetType() == P_NSDT)
|
|
&& ! HasExtraData()
|
|
&& IsFirstContainer() ) {
|
|
ED_Alignment alignment = GetAlignment();
|
|
ED_Alignment defaultAlignment = GetDefaultAlignment();
|
|
if ( alignment == ED_ALIGN_DEFAULT
|
|
|| alignment == defaultAlignment
|
|
|| ( alignment == ED_ALIGN_LEFT
|
|
&& defaultAlignment == ED_ALIGN_DEFAULT )) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
XP_Bool CEditContainerElement::IsFirstContainer(){
|
|
CEditSubDocElement* pSubDoc = GetSubDoc();
|
|
return ! pSubDoc || pSubDoc->GetChild() == this;
|
|
}
|
|
|
|
XP_Bool CEditContainerElement::SupportsAlign(){
|
|
return BitSet( edt_setContainerSupportsAlign, GetType() );
|
|
}
|
|
|
|
|
|
XP_Bool CEditContainerElement::IsImplicitBreak() {
|
|
XP_ASSERT( GetType() == P_NSDT );
|
|
CEditElement *pPrev;
|
|
return ( (pPrev = GetPreviousSibling()) == 0
|
|
|| !pPrev->IsContainer()
|
|
|| pPrev->Container()->ForcesDoubleSpacedNextLine()
|
|
#if 0
|
|
|| ((pPrev->Container()->GetAlignment() == ED_ALIGN_RIGHT) && (GetAlignment() != ED_ALIGN_RIGHT) )
|
|
|| ((pPrev->Container()->GetAlignment() != ED_ALIGN_RIGHT) && (GetAlignment() == ED_ALIGN_RIGHT) )
|
|
#endif
|
|
);
|
|
|
|
}
|
|
|
|
|
|
// 0..2, where 0 = not in pseudo paragraph.
|
|
// 1 = first container of pseudo paragraph.
|
|
// 2 = second container of pseudo paragraph.
|
|
// 3 = after list, <P> does NOT cause a blank line to be drawn above paragraph. we need <P><BR>
|
|
|
|
int16 CEditContainerElement::GetPseudoParagraphState(){
|
|
if( GetType() != P_NSDT ){
|
|
return 0;
|
|
}
|
|
CEditElement *pPrev = GetPreviousSibling();
|
|
CEditElement *pPrevPrev;
|
|
if (pPrev)
|
|
pPrevPrev = pPrev->GetPreviousSibling();
|
|
else
|
|
pPrevPrev=NULL;
|
|
CEditElement *pNext = GetNextSibling();
|
|
if ( !pPrev || (!pPrev->IsContainer()) && !IsEmpty() ) {
|
|
return 0;
|
|
}
|
|
if (!pPrev->IsContainer())
|
|
return 1;
|
|
CEditContainerElement* pPrevContainer = pPrev->Container();
|
|
CEditContainerElement* pPrevPrevContainer=NULL;
|
|
if ((pPrevPrev) &&(pPrevPrev->IsContainer()))
|
|
pPrevPrevContainer = pPrevPrev->Container();
|
|
|
|
if ( IsEmpty() && pPrevContainer->GetType() == P_NSDT && pPrevContainer->IsEmpty()
|
|
&& (!pPrevPrevContainer || pPrevPrevContainer->GetType() != P_NSDT)
|
|
&& pNext && pNext->IsContainer() && pNext->Container()->GetType() == P_NSDT && pNext->Container()->IsEmpty() )
|
|
{
|
|
return 5; //special for 3 spaces. we must settle up the <BR> tags now. <P> later wont cut it.
|
|
}
|
|
|
|
if ( IsEmpty() && pPrevContainer->GetType() == P_NSDT && pPrevContainer->IsEmpty()
|
|
&& (!pPrevPrevContainer || pPrevPrevContainer->GetType() != P_NSDT) )
|
|
{
|
|
return 4;
|
|
}
|
|
if ( !IsEmpty() && pPrevContainer->GetType() == P_NSDT && pPrevContainer->IsEmpty()
|
|
&& (!pPrevPrev || pPrevPrev->GetType()!= P_TABLE)//tables do not enforce a break! <P> is sufficient for 1 break!
|
|
&& (!pPrevPrevContainer || pPrevPrevContainer->GetType() != P_NSDT) )
|
|
{
|
|
return 3;
|
|
}
|
|
if ( ! IsEmpty() && ((pPrevContainer->GetType() == P_NSDT
|
|
&& pPrevContainer->IsEmpty())|| pPrevPrev && pPrevPrev->GetType() == P_TABLE) )
|
|
{
|
|
return 2;
|
|
}
|
|
// Check for state 1
|
|
if ( IsEmpty() && pNext && pNext->IsContainer() &&
|
|
pNext->Container()->GetType() == P_NSDT
|
|
&& ! pNext->Container()->IsEmpty() ) {
|
|
return 1;
|
|
}
|
|
// NOTE: there needs to be a BR tag when the previous non empty container is the same alignment as this one!
|
|
//the helper function is mainly to make sure default and left are equivalent
|
|
pPrevContainer= GetPreviousNonEmptyContainer(); //check nonempty!
|
|
if ( !IsEmpty() && pPrevContainer && !CompareAlignments(pPrevContainer->GetAlignment(),GetAlignment()) )
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
//NOTES:
|
|
/*
|
|
It is not necessary to put a BR tag to break after <CENTER> or <DIV ALIGN=right>.
|
|
there IS an implicit break of sorts at that point
|
|
*/
|
|
void CEditContainerElement::PrintOpen( CPrintState *pPrintState ){
|
|
if ( ShouldSkipTags() ) return;
|
|
|
|
XP_Bool bHasExtraData = HasExtraData();
|
|
|
|
#ifdef EDT_DDT
|
|
if( GetType() == P_NSDT ){
|
|
int16 ppState = GetPseudoParagraphState();
|
|
CEditContainerElement* pPrevContainer=GetPreviousNonEmptyContainer();
|
|
if (pPrevContainer && pPrevContainer->GetType()!=P_NSDT)
|
|
pPrevContainer=NULL;//all bets are off
|
|
|
|
// We must always set alignment explicitly for text in TableCaptions
|
|
// For this to work, we must change use ED_ALIGN_DEFAULT instead of ED_ALIGN_LEFT
|
|
// when parsing regular paragraphs so we don't clutter HTML with extra <DIV> alignment tags
|
|
if (( GetAlignment() == ED_ALIGN_LEFT && ! IsEmpty()) && ( !pPrevContainer ||
|
|
(pPrevContainer->GetAlignment() != ED_ALIGN_LEFT) ))
|
|
{
|
|
pPrintState->m_pOut->Printf( "\n");
|
|
pPrintState->m_iCharPos = pPrintState->m_pOut->Printf( "<DIV ALIGN=left>");
|
|
|
|
}
|
|
else if (( GetAlignment() == ED_ALIGN_RIGHT && ! IsEmpty()) && ( !pPrevContainer || (pPrevContainer->GetAlignment() != ED_ALIGN_RIGHT ) ))
|
|
{
|
|
pPrintState->m_pOut->Printf( "\n");
|
|
pPrintState->m_iCharPos = pPrintState->m_pOut->Printf( "<DIV ALIGN=right>");
|
|
|
|
}
|
|
else if (( GetAlignment() == ED_ALIGN_ABSCENTER && !IsEmpty()) && ( !pPrevContainer || (pPrevContainer->GetAlignment() != ED_ALIGN_ABSCENTER ) ))
|
|
{
|
|
pPrintState->m_pOut->Printf( "\n");
|
|
pPrintState->m_iCharPos = pPrintState->m_pOut->Printf( "<CENTER>");
|
|
}
|
|
if( !IsImplicitBreak() && ! bHasExtraData)
|
|
{
|
|
switch( ppState ){
|
|
default:
|
|
case 0:
|
|
pPrintState->m_pOut->Printf( "\n");
|
|
pPrintState->m_iCharPos = pPrintState->m_pOut->Printf("<BR>");
|
|
break;
|
|
case 1:
|
|
// Do nothing. This is an empty paragraph.
|
|
break;
|
|
case 2:
|
|
pPrintState->m_pOut->Printf( "\n");
|
|
pPrintState->m_iCharPos = pPrintState->m_pOut->Printf( "<P>");
|
|
break;
|
|
case 3:
|
|
pPrintState->m_pOut->Printf( "\n"); //used for 1 space after listitem.
|
|
pPrintState->m_iCharPos = pPrintState->m_pOut->Printf( "<P><BR>");
|
|
break;
|
|
case 4:
|
|
pPrintState->m_pOut->Printf( "\n"); //used for 2 open spaces after listitems for example
|
|
pPrintState->m_iCharPos = pPrintState->m_pOut->Printf( "<P> "); //only gives us one.
|
|
//if <P> is comming, we will get 2 line spacing. if we wanted more,
|
|
//we should have settled up with a 5 on return from pseudo
|
|
break;
|
|
case 5:
|
|
pPrintState->m_pOut->Printf( "\n"); //used for 3 open spaces after listitems for example
|
|
pPrintState->m_pOut->Printf( "<P> "); //gives 2 spaces after list type item
|
|
pPrintState->m_pOut->Printf( "\n");
|
|
pPrintState->m_iCharPos = pPrintState->m_pOut->Printf( "<BR> ");
|
|
break;
|
|
}
|
|
}
|
|
if ( bHasExtraData ) {
|
|
EDT_ContainerData* pData = GetData();
|
|
if ( pData && pData->pExtra ) {
|
|
pPrintState->m_pOut->Printf( "\n");
|
|
if (ppState == 2) pPrintState->m_pOut->Printf( "\n");
|
|
char* pTagName = ppState == 2 ? "P" : "DIV";
|
|
pPrintState->m_iCharPos = pPrintState->m_pOut->Printf( "<%s %s>", pTagName, pData->pExtra);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
pPrintState->m_iCharPos = 0;
|
|
pPrintState->m_pOut->Printf("\n");
|
|
|
|
PA_Tag *pTag = TagOpen(0);
|
|
while( pTag ){
|
|
char *pData = 0;
|
|
if( pTag->data ){
|
|
PA_LOCK( pData, char*, pTag->data );
|
|
}
|
|
|
|
if( pData && *pData != '>' ) {
|
|
pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "<%s %s",
|
|
EDT_TagString(pTag->type), pData );
|
|
}
|
|
else {
|
|
pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "<%s>",
|
|
EDT_TagString(pTag->type));
|
|
}
|
|
|
|
if( pTag->data ){
|
|
PA_UNLOCK( pTag->data );
|
|
}
|
|
|
|
PA_Tag* pDelTag = pTag;
|
|
pTag = pTag->next;
|
|
PA_FreeTag(pDelTag);
|
|
|
|
if( GetType() != P_PREFORMAT ){
|
|
pPrintState->m_pOut->Printf( "\n");
|
|
pPrintState->m_iCharPos = 0;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
XP_Bool CEditContainerElement::ShouldSkipTags(){
|
|
XP_Bool parentIsTableCell = GetParent() && GetParent()->IsTableCell();
|
|
return parentIsTableCell && IsPlainFirstContainer();
|
|
}
|
|
|
|
XP_Bool CEditContainerElement::HasExtraData(){
|
|
// Does this have data other than the ALIGN tag?
|
|
XP_Bool hasData = FALSE;
|
|
EDT_ContainerData* pData = GetData();
|
|
if ( pData ) {
|
|
if ( pData->pExtra ) {
|
|
hasData = TRUE;
|
|
}
|
|
FreeData(pData);
|
|
}
|
|
return hasData;
|
|
}
|
|
|
|
void CEditContainerElement::PrintEnd( CPrintState *pPrintState ){
|
|
if ( ShouldSkipTags() ) return;
|
|
|
|
#ifdef EDT_DDT
|
|
if( GetType() == P_NSDT ){
|
|
int16 ppState = GetPseudoParagraphState();
|
|
XP_Bool bNeedReturn = FALSE;
|
|
CEditContainerElement* pNextContainer=GetNextNonEmptyContainer();
|
|
if ( HasExtraData() ){
|
|
if ( ppState != 2 ){
|
|
pPrintState->m_pOut->Printf( "</DIV>");
|
|
bNeedReturn = TRUE;
|
|
}
|
|
}
|
|
// Keep empty paragraphs from being eaten
|
|
if ( IsEmpty() && ppState == 0)
|
|
{
|
|
char* space = " ";
|
|
pPrintState->m_pOut->Printf( space );
|
|
pPrintState->m_iCharPos += XP_STRLEN(space);
|
|
}
|
|
ED_Alignment align = GetAlignment();
|
|
if( !IsEmpty() && (align == ED_ALIGN_RIGHT || align == ED_ALIGN_LEFT) &&
|
|
(!pNextContainer || pNextContainer->GetType() != P_NSDT ||
|
|
!CompareAlignments(pNextContainer->GetAlignment(), align)) )
|
|
{
|
|
pPrintState->m_pOut->Printf( "</DIV>");
|
|
bNeedReturn = TRUE;
|
|
}
|
|
else if(( GetAlignment() == ED_ALIGN_ABSCENTER && !IsEmpty() ) &&
|
|
(!pNextContainer || pNextContainer->GetType() != P_NSDT || !CompareAlignments(pNextContainer->GetAlignment(), align)) )
|
|
{
|
|
pPrintState->m_pOut->Printf( "</CENTER>");
|
|
bNeedReturn = TRUE;
|
|
}
|
|
|
|
if ( bNeedReturn )
|
|
{
|
|
pPrintState->m_iCharPos = 0;
|
|
pPrintState->m_pOut->Printf( "\n");
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
|
|
PA_Tag *pTag = TagEnd();
|
|
while( pTag ){
|
|
char *pData = 0;
|
|
if( pTag->data ){
|
|
PA_LOCK( pData, char*, pTag->data );
|
|
}
|
|
|
|
pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "</%s>",
|
|
EDT_TagString( pTag->type ) );
|
|
|
|
if( pTag->data ){
|
|
PA_UNLOCK( pTag->data );
|
|
}
|
|
|
|
PA_Tag* pDelTag = pTag;
|
|
pTag = pTag->next;
|
|
PA_FreeTag(pDelTag);
|
|
}
|
|
|
|
pPrintState->m_iCharPos = 0;
|
|
pPrintState->m_pOut->Printf( "\n");
|
|
}
|
|
|
|
|
|
|
|
CEditElement* CEditContainerElement::Clone( CEditElement* pParent ){
|
|
PA_Tag *pTag = TagOpen(0);
|
|
|
|
// LTNOTE: we get division tags at the beginning of paragraphs
|
|
// for their alignment. Ignore these tags, we get it directly from the
|
|
// container itself.
|
|
// Its a total hack.
|
|
//
|
|
if( pTag->type == P_DIVISION || pTag->type == P_CENTER ){
|
|
PA_Tag *pDelTag = pTag;
|
|
pTag = pTag->next;
|
|
PA_FreeTag( pDelTag );
|
|
}
|
|
CEditElement *pRet = new CEditContainerElement(pParent, pTag, GetWinCSID(), m_align );
|
|
PA_FreeTag( pTag );
|
|
return pRet;
|
|
}
|
|
|
|
// Property Getting and Setting Stuff.
|
|
|
|
EDT_ContainerData* CEditContainerElement::NewData(){
|
|
EDT_ContainerData *pData = XP_NEW( EDT_ContainerData );
|
|
pData->align = ED_ALIGN_LEFT;
|
|
pData->pExtra = 0;
|
|
return pData;
|
|
}
|
|
|
|
void CEditContainerElement::FreeData( EDT_ContainerData *pData ){
|
|
if ( pData ) {
|
|
XP_FREEIF(pData->pExtra);
|
|
XP_FREE( pData );
|
|
}
|
|
}
|
|
|
|
void CEditContainerElement::SetData( EDT_ContainerData *pData ){
|
|
char *pNew = 0;
|
|
|
|
m_align = pData->align;
|
|
if( m_align == ED_ALIGN_CENTER ) m_align = ED_ALIGN_ABSCENTER;
|
|
|
|
//
|
|
// Never generate ALIGN= stuff anymore.
|
|
// Use the Div tag for LEFT and RIGHT, and the CENTER tag
|
|
//
|
|
char* pExtra = "";
|
|
if ( pData && pData->pExtra ) {
|
|
pExtra = pData->pExtra;
|
|
}
|
|
pNew = PR_sprintf_append( pNew, "%s>", pExtra );
|
|
|
|
SetTagData( pNew );
|
|
free(pNew);
|
|
}
|
|
|
|
EDT_ContainerData* CEditContainerElement::GetData( ){
|
|
EDT_ContainerData *pRet;
|
|
PA_Tag* pTag = TagOpen(0);
|
|
pRet = ParseParams( pTag, GetWinCSID() );
|
|
EDT_DeleteTagChain( pTag );
|
|
return pRet;
|
|
}
|
|
|
|
static char* containerParams[] = {
|
|
PARAM_ALIGN,
|
|
"nscisaw",
|
|
0
|
|
};
|
|
|
|
EDT_ContainerData* CEditContainerElement::ParseParams( PA_Tag *pTag, int16 csid ){
|
|
EDT_ContainerData *pData = NewData();
|
|
ED_Alignment align;
|
|
|
|
m_hackPreformat = edt_FetchParamBoolExist(pTag, "nscisaw", csid);
|
|
|
|
align = edt_FetchParamAlignment( pTag, m_defaultAlign, FALSE, csid );
|
|
if( align == ED_ALIGN_CENTER ) align = ED_ALIGN_ABSCENTER;
|
|
|
|
if( align == ED_ALIGN_DEFAULT || align == ED_ALIGN_RIGHT || align == ED_ALIGN_LEFT || align == ED_ALIGN_ABSCENTER){
|
|
pData->align = align;
|
|
}
|
|
pData->pExtra = edt_FetchParamExtras( pTag, containerParams, csid );
|
|
return pData;
|
|
}
|
|
|
|
void CEditContainerElement::SetAlignment( ED_Alignment eAlign ){
|
|
EDT_ContainerData *pData = GetData();
|
|
pData->align = eAlign;
|
|
SetData( pData );
|
|
FreeData( pData );
|
|
}
|
|
|
|
void CEditContainerElement::CopyAttributes(CEditContainerElement *pFrom ){
|
|
SetType( pFrom->GetType() );
|
|
SetAlignment( pFrom->GetAlignment() );
|
|
}
|
|
|
|
XP_Bool CEditContainerElement::IsEmpty(){
|
|
CEditElement *pChild = GetChild();
|
|
return ( pChild == 0
|
|
|| ( pChild->IsA( P_TEXT )
|
|
&& pChild->Text()->GetLen() == 0
|
|
&& pChild->LeafInContainerAfter() == 0 )
|
|
);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void CEditContainerElement::ValidateTree(){
|
|
CEditElement::ValidateTree();
|
|
CEditElement* pChild = GetChild();
|
|
XP_ASSERT(pChild); // Must have at least one child.
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Sets the alignment if the container is empty (or almost empty). Used only
|
|
// during the parse phase to handle things like <p><center><img>
|
|
//
|
|
void CEditContainerElement::AlignIfEmpty( ED_Alignment eAlign ){
|
|
CEditElement *pChild = GetChild();
|
|
while( pChild ){
|
|
if( pChild->IsA( P_TEXT )
|
|
&& pChild->Leaf()->GetLen() != 0 ){
|
|
return;
|
|
}
|
|
pChild = pChild->GetNextSibling();
|
|
}
|
|
SetAlignment( eAlign );
|
|
}
|
|
|
|
void CEditContainerElement::StreamToPositionalText( IStreamOut *pOut, XP_Bool bEnd ){
|
|
if( bEnd ){
|
|
pOut->Write(" ",1);
|
|
}
|
|
}
|
|
|
|
char* CEditContainerElement::GetText()
|
|
{
|
|
char *pText = NULL;
|
|
CEditElement *pChild = GetChild();
|
|
while( pChild )
|
|
{
|
|
if( pChild->IsText() && pChild->Leaf()->GetLen() != 0 )
|
|
{
|
|
pText = PR_sprintf_append( pText, pChild->Text()->GetText() );
|
|
}
|
|
pChild = pChild->GetNextSibling();
|
|
}
|
|
return pText;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CEditListElement
|
|
//-----------------------------------------------------------------------------
|
|
|
|
CEditListElement::CEditListElement(
|
|
CEditElement *pParent, PA_Tag *pTag, int16 /*csid*/ ):
|
|
CEditElement( pParent, pTag->type ){
|
|
|
|
if( pTag ){
|
|
char *locked_buff;
|
|
|
|
PA_LOCK(locked_buff, char *, pTag->data );
|
|
SetTagData( locked_buff );
|
|
PA_UNLOCK(pTag->data);
|
|
}
|
|
m_pBaseURL = NULL;
|
|
}
|
|
|
|
CEditListElement::CEditListElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer):
|
|
CEditElement( pStreamIn, pBuffer )
|
|
{
|
|
m_pBaseURL = NULL;
|
|
}
|
|
|
|
CEditListElement::~CEditListElement()
|
|
{
|
|
XP_FREEIF(m_pBaseURL);
|
|
}
|
|
|
|
PA_Tag* CEditListElement::TagOpen( int /* iEditOffset */ ){
|
|
PA_Tag *pTag = XP_NEW( PA_Tag );
|
|
XP_BZERO( pTag, sizeof( PA_Tag ) );
|
|
SetTagData( pTag, GetTagData() ? GetTagData() : ">" );
|
|
return pTag;
|
|
}
|
|
|
|
CEditElement* CEditListElement::Clone( CEditElement *pParent ){
|
|
PA_Tag* pTag = TagOpen(0);
|
|
CEditElement* pNew = new CEditListElement( pParent, pTag, GetWinCSID() );
|
|
PA_FreeTag( pTag );
|
|
return pNew;
|
|
}
|
|
|
|
XP_Bool CEditListElement::IsCompatableList(CEditElement *pElement)
|
|
{
|
|
if( pElement && pElement->IsList() )
|
|
{
|
|
TagType t = pElement->GetType();
|
|
if( t == m_tagType ||
|
|
((t == P_UNUM_LIST && m_tagType == P_NUM_LIST) ||
|
|
(m_tagType == P_UNUM_LIST && t == P_NUM_LIST)) )
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// Property Getting and Setting Stuff.
|
|
|
|
EDT_ListData* CEditListElement::NewData(){
|
|
EDT_ListData *pData = XP_NEW( EDT_ListData );
|
|
if( pData == 0 ){
|
|
// throw();
|
|
return pData;
|
|
}
|
|
pData->iTagType = P_UNUM_LIST;
|
|
pData->bCompact = FALSE;
|
|
pData->eType = ED_LIST_TYPE_DEFAULT;
|
|
pData->iStart = 1;
|
|
pData->pExtra = 0;
|
|
pData->pBaseURL = NULL;
|
|
return pData;
|
|
}
|
|
|
|
void CEditListElement::FreeData( EDT_ListData *pData ){
|
|
if( pData->pExtra ) XP_FREE( pData->pExtra );
|
|
XP_FREEIF(pData->pBaseURL);
|
|
XP_FREE( pData );
|
|
}
|
|
|
|
void CEditListElement::SetData( EDT_ListData *pData ){
|
|
char *pNew = 0;
|
|
|
|
// Copy in pBaseURL.
|
|
XP_FREEIF(m_pBaseURL);
|
|
if (pData->pBaseURL && *pData->pBaseURL) {
|
|
m_pBaseURL = XP_STRDUP(pData->pBaseURL);
|
|
}
|
|
|
|
SetType( pData->iTagType );
|
|
|
|
if( pData->bCompact ){
|
|
pNew = PR_sprintf_append( pNew, " COMPACT");
|
|
}
|
|
if( pData->iStart != 1 ){
|
|
pNew = PR_sprintf_append( pNew, " START=%d", pData->iStart );
|
|
}
|
|
switch( pData->eType ){
|
|
case ED_LIST_TYPE_DEFAULT:
|
|
break;
|
|
case ED_LIST_TYPE_DIGIT:
|
|
SetType( P_NUM_LIST );
|
|
break;
|
|
case ED_LIST_TYPE_BIG_ROMAN:
|
|
SetType( P_NUM_LIST );
|
|
pNew = PR_sprintf_append( pNew, " TYPE=I" );
|
|
break;
|
|
case ED_LIST_TYPE_SMALL_ROMAN:
|
|
SetType( P_NUM_LIST );
|
|
pNew = PR_sprintf_append( pNew, " TYPE=i" );
|
|
break;
|
|
case ED_LIST_TYPE_BIG_LETTERS:
|
|
SetType( P_NUM_LIST );
|
|
pNew = PR_sprintf_append( pNew, " TYPE=A" );
|
|
break;
|
|
case ED_LIST_TYPE_SMALL_LETTERS:
|
|
SetType( P_NUM_LIST );
|
|
pNew = PR_sprintf_append( pNew, " TYPE=a" );
|
|
break;
|
|
case ED_LIST_TYPE_CIRCLE:
|
|
SetType( P_UNUM_LIST );
|
|
pNew = PR_sprintf_append( pNew, " TYPE=CIRCLE" );
|
|
break;
|
|
case ED_LIST_TYPE_SQUARE:
|
|
SetType( P_UNUM_LIST );
|
|
pNew = PR_sprintf_append( pNew, " TYPE=SQUARE" );
|
|
break;
|
|
case ED_LIST_TYPE_DISC:
|
|
SetType( P_UNUM_LIST );
|
|
pNew = PR_sprintf_append( pNew, " TYPE=DISC" );
|
|
break;
|
|
case ED_LIST_TYPE_CITE:
|
|
SetType( P_BLOCKQUOTE );
|
|
pNew = PR_sprintf_append( pNew, " TYPE=CITE" );
|
|
break;
|
|
}
|
|
|
|
if( pData->pExtra ){
|
|
pNew = PR_sprintf_append( pNew, " %s", pData->pExtra );
|
|
}
|
|
|
|
if( pNew ){
|
|
pNew = PR_sprintf_append( pNew, ">" );
|
|
}
|
|
|
|
SetTagData( pNew );
|
|
|
|
XP_FREEIF(pNew);
|
|
}
|
|
|
|
EDT_ListData* CEditListElement::GetData( ){
|
|
EDT_ListData *pRet;
|
|
PA_Tag* pTag = TagOpen(0);
|
|
pRet = ParseParams( pTag, GetWinCSID() );
|
|
PA_FreeTag( pTag );
|
|
|
|
if (pRet && m_pBaseURL) {
|
|
pRet->pBaseURL = XP_STRDUP(m_pBaseURL);
|
|
}
|
|
return pRet;
|
|
}
|
|
|
|
static char* listParams[] = {
|
|
PARAM_TYPE,
|
|
PARAM_COMPACT,
|
|
PARAM_START,
|
|
0
|
|
};
|
|
|
|
EDT_ListData* CEditListElement::ParseParams( PA_Tag *pTag, int16 csid ){
|
|
EDT_ListData *pData = NewData();
|
|
char *pType;
|
|
|
|
pData->iTagType = pTag->type;
|
|
pData->bCompact = edt_FetchParamBoolExist( pTag, PARAM_COMPACT, csid );
|
|
|
|
pType = edt_FetchParamString( pTag, PARAM_TYPE, csid );
|
|
if( pType == 0 ){
|
|
if( pTag->type == P_NUM_LIST ){
|
|
pData->eType = ED_LIST_TYPE_DIGIT;
|
|
}
|
|
else {
|
|
pData->eType = ED_LIST_TYPE_DEFAULT;
|
|
}
|
|
}
|
|
else {
|
|
switch (*pType){
|
|
case 'A':
|
|
pData->eType = ED_LIST_TYPE_BIG_LETTERS;
|
|
break;
|
|
case 'a':
|
|
pData->eType = ED_LIST_TYPE_SMALL_LETTERS;
|
|
break;
|
|
case 'i':
|
|
pData->eType = ED_LIST_TYPE_SMALL_ROMAN;
|
|
break;
|
|
case 'I':
|
|
pData->eType = ED_LIST_TYPE_BIG_ROMAN;
|
|
break;
|
|
case 'c':
|
|
case 'C':
|
|
if ( XP_STRNCASECMP(pType, "cite", 4) == 0) {
|
|
pData->eType = ED_LIST_TYPE_CITE;
|
|
}
|
|
else {
|
|
pData->eType = ED_LIST_TYPE_CIRCLE;
|
|
}
|
|
break;
|
|
case 'd':
|
|
case 'D':
|
|
pData->eType = ED_LIST_TYPE_DISC;
|
|
break;
|
|
case 's':
|
|
case 'S':
|
|
pData->eType = ED_LIST_TYPE_SQUARE;
|
|
break;
|
|
default:
|
|
// To do: Figure out how to preserve unknown list types.
|
|
pData->eType = ED_LIST_TYPE_DEFAULT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pData->iStart = edt_FetchParamInt( pTag, PARAM_START, 1, csid );
|
|
pData->pExtra = edt_FetchParamExtras( pTag, listParams, csid );
|
|
return pData;
|
|
}
|
|
|
|
void CEditListElement::CopyData(CEditListElement* pOther){
|
|
EDT_ListData* pData = pOther->GetData();
|
|
SetData(pData);
|
|
FreeData(pData);
|
|
}
|
|
|
|
XP_Bool CEditListElement::IsMailQuote(){
|
|
XP_Bool bResult = FALSE;
|
|
EDT_ListData* pData = GetData();
|
|
if ( pData->iTagType == P_BLOCKQUOTE &&
|
|
pData->eType == ED_LIST_TYPE_CITE ) {
|
|
bResult = TRUE;
|
|
}
|
|
FreeData(pData);
|
|
return bResult;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void CEditListElement::ValidateTree(){
|
|
CEditElement::ValidateTree();
|
|
// Must have at least one child.
|
|
XP_ASSERT(GetChild());
|
|
// We rely on the children to check that
|
|
// they can have a list as a parent.
|
|
}
|
|
#endif
|
|
|
|
EEditElementType CEditListElement::GetElementType(){
|
|
return eListElement;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CEditTextElement
|
|
//-----------------------------------------------------------------------------
|
|
|
|
CEditTextElement::CEditTextElement(CEditElement *pParent, char *pText):
|
|
CEditLeafElement(pParent, P_TEXT),
|
|
m_pFirstLayoutElement(0),
|
|
m_pTextBlock(0),
|
|
m_tf(TF_NONE),
|
|
m_iFontSize(DEF_FONTSIZE),
|
|
m_href(ED_LINK_ID_NONE),
|
|
m_color(ED_Color::GetUndefined()),
|
|
m_pFace(NULL),
|
|
m_iWeight(ED_FONT_WEIGHT_NORMAL),
|
|
m_iPointSize(ED_FONT_POINT_SIZE_DEFAULT),
|
|
m_pScriptExtra(NULL)
|
|
|
|
{
|
|
if( pText && *pText ){
|
|
m_pText = XP_STRDUP(pText); // should be new??
|
|
m_textSize = XP_STRLEN(m_pText)+1;
|
|
}
|
|
else {
|
|
m_pText = 0;
|
|
m_textSize = 0;
|
|
}
|
|
}
|
|
|
|
CEditTextElement::CEditTextElement( IStreamIn *pIn, CEditBuffer* pBuffer ):
|
|
CEditLeafElement(pIn, pBuffer),
|
|
m_pFirstLayoutElement(0),
|
|
m_pTextBlock(0),
|
|
m_tf(TF_NONE),
|
|
m_iFontSize(DEF_FONTSIZE),
|
|
m_href(ED_LINK_ID_NONE),
|
|
m_pText(0),
|
|
m_color(ED_Color::GetUndefined()),
|
|
m_pFace(NULL),
|
|
m_pScriptExtra(NULL)
|
|
{
|
|
m_tf = (ED_TextFormat)pIn->ReadInt( );
|
|
|
|
m_iFontSize = pIn->ReadInt( );
|
|
|
|
m_color = pIn->ReadInt( );
|
|
|
|
if( m_tf & TF_HREF ){
|
|
char* pHref = pIn->ReadZString();
|
|
char* pExtra = pIn->ReadZString();
|
|
SetHREF( pBuffer->linkManager.Add( pHref, pExtra ));
|
|
XP_FREE( pHref );
|
|
XP_FREE( pExtra );
|
|
}
|
|
|
|
if ( m_tf & TF_FONT_FACE ) {
|
|
m_pFace = pIn->ReadZString();
|
|
}
|
|
|
|
if ( m_tf & TF_FONT_WEIGHT ) {
|
|
m_iWeight = (int16) pIn->ReadInt();
|
|
}
|
|
|
|
if ( m_tf & TF_FONT_POINT_SIZE ) {
|
|
m_iPointSize = (int16) pIn->ReadInt();
|
|
}
|
|
|
|
if ( EDT_IS_SCRIPT(m_tf) ) {
|
|
m_pScriptExtra = pIn->ReadZString();
|
|
}
|
|
|
|
m_pText = pIn->ReadZString();
|
|
if( m_pText ){
|
|
m_textSize = XP_STRLEN( m_pText );
|
|
}
|
|
else {
|
|
m_textSize = 0;
|
|
}
|
|
}
|
|
|
|
CEditTextElement::~CEditTextElement(){
|
|
DisconnectLayoutElements((LO_Element*) m_pFirstLayoutElement);
|
|
if ( m_pText) XP_FREE(m_pText);
|
|
if( m_href != ED_LINK_ID_NONE ) m_href->Release();
|
|
if ( m_pFace ) {
|
|
XP_FREE(m_pFace);
|
|
m_pFace = NULL;
|
|
}
|
|
if ( m_pScriptExtra ) {
|
|
XP_FREE(m_pScriptExtra);
|
|
m_pScriptExtra = NULL;
|
|
}
|
|
}
|
|
|
|
XP_Bool CEditTextElement::GetLOElementAndOffset( ElementOffset iEditOffset, XP_Bool bEditStickyAfter,
|
|
LO_Element*& pRetText,
|
|
int& pLayoutOffset ){
|
|
return GetLOTextAndOffset( iEditOffset, bEditStickyAfter, *(LO_TextStruct**)&pRetText, pLayoutOffset);
|
|
}
|
|
|
|
|
|
void CEditTextElement::StreamOut( IStreamOut *pOut ){
|
|
CEditSelection all;
|
|
GetAll(all);
|
|
PartialStreamOut(pOut, all );
|
|
}
|
|
|
|
void CEditTextElement::PartialStreamOut( IStreamOut *pOut, CEditSelection& selection ){
|
|
CEditSelection local;
|
|
if ( ClipToMe(selection, local) ) {
|
|
|
|
// Fake a stream out.
|
|
CEditLeafElement::StreamOut( pOut );
|
|
|
|
pOut->WriteInt( m_tf );
|
|
|
|
pOut->WriteInt( m_iFontSize );
|
|
|
|
pOut->WriteInt( m_color.GetAsLong() );
|
|
|
|
if( m_tf & TF_HREF )
|
|
{
|
|
// Must make URLs absolute when copying
|
|
CEditBuffer *pBuffer = GetEditBuffer();
|
|
char *pAbsolute = NULL;
|
|
if( pBuffer )
|
|
pAbsolute = NET_MakeAbsoluteURL(LO_GetBaseURL(pBuffer->m_pContext), m_href->hrefStr);
|
|
|
|
if( pAbsolute )
|
|
{
|
|
pOut->WriteZString( pAbsolute );
|
|
XP_FREE(pAbsolute);
|
|
}
|
|
else
|
|
pOut->WriteZString( m_href->hrefStr );
|
|
|
|
pOut->WriteZString( m_href->pExtra );
|
|
}
|
|
|
|
if( m_tf & TF_FONT_FACE ){
|
|
pOut->WriteZString( m_pFace );
|
|
}
|
|
if( m_tf & TF_FONT_WEIGHT ){
|
|
pOut->WriteInt(m_iWeight);
|
|
}
|
|
if( m_tf & TF_FONT_POINT_SIZE ){
|
|
pOut->WriteInt(m_iPointSize);
|
|
}
|
|
|
|
if( EDT_IS_SCRIPT(m_tf) ){
|
|
pOut->WriteZString( m_pScriptExtra );
|
|
}
|
|
|
|
pOut->WritePartialZString( m_pText, local.m_start.m_iPos, local.m_end.m_iPos);
|
|
|
|
// No children
|
|
pOut->WriteInt((int32)eElementNone);
|
|
}
|
|
}
|
|
|
|
|
|
EEditElementType CEditTextElement::GetElementType()
|
|
{
|
|
return eTextElement;
|
|
}
|
|
|
|
void CEditTextElement::SetLayoutElement( intn iEditOffset, intn lo_type,
|
|
LO_Element* pLoElement ){
|
|
|
|
/* this element might be our text block. if so, we want to keep track of it */
|
|
if ( lo_type == LO_TEXTBLOCK ){
|
|
#if defined( DEBUG_shannon )
|
|
XP_TRACE(("Setting text block to %lx", pLoElement));
|
|
#endif
|
|
XP_ASSERT ( pLoElement != NULL );
|
|
m_pTextBlock = &pLoElement->lo_textBlock;
|
|
pLoElement->lo_any.edit_element = this;
|
|
pLoElement->lo_any.edit_offset = iEditOffset;
|
|
}
|
|
|
|
SetLayoutElementHelper(LO_TEXT, (LO_Element**) &m_pFirstLayoutElement,
|
|
iEditOffset, lo_type, pLoElement);
|
|
}
|
|
|
|
LO_Element* CEditTextElement::GetLayoutElement()
|
|
{
|
|
return m_pFirstLayoutElement;
|
|
}
|
|
|
|
void CEditTextElement::ResetLayoutElement( intn iEditOffset,
|
|
LO_Element* pLoElement ){
|
|
ResetLayoutElementHelper((LO_Element**) &m_pFirstLayoutElement,
|
|
iEditOffset, pLoElement);
|
|
}
|
|
|
|
void edt_RemoveNBSP( int16 csid, char *pString ){
|
|
while( pString && *pString ){
|
|
if( *pString == NON_BREAKING_SPACE ){
|
|
*pString = ' ';
|
|
}
|
|
pString = INTL_NextChar(csid, pString);
|
|
}
|
|
}
|
|
|
|
void CEditTextElement::SetText( char *pText, XP_Bool bConvertSpaces, int16 csid){
|
|
if( m_pText ){
|
|
XP_FREE( m_pText );
|
|
}
|
|
if( pText && *pText ){
|
|
m_pText = XP_STRDUP(pText); // should be new??
|
|
m_textSize = XP_STRLEN(m_pText)+1;
|
|
if( bConvertSpaces ){
|
|
edt_RemoveNBSP( csid, m_pText );
|
|
}
|
|
}
|
|
else {
|
|
m_pText = 0;
|
|
m_textSize = 0;
|
|
}
|
|
}
|
|
|
|
|
|
void CEditTextElement::SetColor( ED_Color iColor ){
|
|
m_color = iColor;
|
|
// Dont set color if we are a script
|
|
if(!iColor.IsDefined() || EDT_IS_SCRIPT(m_tf) ){
|
|
m_tf &= ~TF_FONT_COLOR;
|
|
}
|
|
else {
|
|
m_tf |= TF_FONT_COLOR;
|
|
}
|
|
}
|
|
|
|
// We seem to have problems when point size gets REALLY big,
|
|
// so limit this for now
|
|
#define ED_MAX_POINT_SIZE 104
|
|
|
|
// Boost the size changes when increasing or decreasing point sizes
|
|
static void AdjustPointSizeChange( int& iChange, int iPointSize )
|
|
{
|
|
if( iChange == 1 )
|
|
{
|
|
if( iPointSize >= 10 && iPointSize <= 24 )
|
|
iChange = 2;
|
|
else if( iPointSize > 24 )
|
|
iChange = 4;
|
|
}
|
|
else if( iChange == -1 )
|
|
{
|
|
if( iPointSize >= 10 && iPointSize <= 24 )
|
|
iChange = -2;
|
|
else if( iPointSize > 24 )
|
|
iChange = -4;
|
|
}
|
|
}
|
|
|
|
void CEditTextElement::SetFontSize( int iSize, XP_Bool bRelative )
|
|
{
|
|
//Override (use default for) ALL font size requests for Java Script
|
|
if( EDT_IS_SCRIPT(m_tf) ){
|
|
iSize = 0;
|
|
}
|
|
else
|
|
{
|
|
// Calculate the new size and keep within allowable range
|
|
// bRelative = TRUE means that iSize is a change relative
|
|
// to current size
|
|
if( bRelative )
|
|
{
|
|
if( m_iPointSize != ED_FONT_POINT_SIZE_DEFAULT )
|
|
{
|
|
// We are already using pts, so change that instead of m_iFontSize
|
|
AdjustPointSizeChange( iSize, m_iPointSize );
|
|
SetFontPointSize(min(ED_MAX_POINT_SIZE, max(MIN_POINT_SIZE, m_iPointSize+iSize)));
|
|
return;
|
|
}
|
|
#ifdef XP_WIN
|
|
// EXPERIMENTAL:
|
|
// If we are trying to increase past the highest HTML size (7),
|
|
// we change over to POINT size instead so we can
|
|
// continue to increase the size.
|
|
// To do this, we need to get the point size of the "7" font,
|
|
if( (m_iFontSize + iSize) > MAX_FONT_SIZE )
|
|
{
|
|
int16 iPointSize = FE_CalcFontPointSize(GetEditBuffer()->m_pContext, MAX_FONT_SIZE, m_tf & TF_FIXED);
|
|
if( iPointSize )
|
|
{
|
|
AdjustPointSizeChange( iSize, iPointSize );
|
|
SetFontPointSize(min(ED_MAX_POINT_SIZE, iPointSize+iSize));
|
|
return;
|
|
}
|
|
}
|
|
#endif //XP_WIN
|
|
// We are using relative scale,
|
|
// set new size relative to current
|
|
iSize = m_iFontSize + iSize;
|
|
}
|
|
}
|
|
iSize = min(MAX_FONT_SIZE, max(MIN_FONT_SIZE,(iSize)));
|
|
|
|
// No change from current state
|
|
if( m_iFontSize == iSize )
|
|
return;
|
|
|
|
m_iFontSize = iSize;
|
|
|
|
|
|
// Use 0 (or less) to signify changing to default
|
|
if( iSize <= 0 ){
|
|
m_iFontSize = GetDefaultFontSize();
|
|
}
|
|
if( m_iFontSize == GetDefaultFontSize() ){
|
|
m_tf &= ~TF_FONT_SIZE;
|
|
}
|
|
else {
|
|
m_tf |= TF_FONT_SIZE;
|
|
}
|
|
// If using relative font size, then absolute point size must be default
|
|
// Note that we do this even if setting to default size,
|
|
// so this overrides any existing point size
|
|
m_tf &= ~TF_FONT_POINT_SIZE;
|
|
m_iPointSize = ED_FONT_POINT_SIZE_DEFAULT;
|
|
}
|
|
|
|
void CEditTextElement::SetFontFace( char* pFace ){
|
|
if ( m_pFace ) {
|
|
XP_FREE(m_pFace);
|
|
m_pFace = NULL;
|
|
m_tf &= ~TF_FONT_FACE;
|
|
}
|
|
if ( pFace ) {
|
|
m_tf |= TF_FONT_FACE;
|
|
// Skip over initial separator signal
|
|
if( *pFace == '_' ){
|
|
pFace++;
|
|
}
|
|
m_pFace = XP_STRDUP(pFace);
|
|
}
|
|
}
|
|
|
|
void CEditTextElement::SetFontWeight( int16 weight ){
|
|
if ( m_iWeight != ED_FONT_WEIGHT_NORMAL ) {
|
|
m_tf &= ~TF_FONT_WEIGHT;
|
|
m_iWeight = ED_FONT_WEIGHT_NORMAL;
|
|
}
|
|
if ( weight != ED_FONT_WEIGHT_NORMAL ) {
|
|
m_tf |= TF_FONT_WEIGHT;
|
|
m_iWeight = weight;
|
|
}
|
|
}
|
|
|
|
void CEditTextElement::SetFontPointSize( int16 pointSize ){
|
|
if ( m_iPointSize != ED_FONT_POINT_SIZE_DEFAULT ) {
|
|
m_tf &= ~TF_FONT_POINT_SIZE;
|
|
m_iPointSize = ED_FONT_POINT_SIZE_DEFAULT;
|
|
}
|
|
if ( pointSize != ED_FONT_POINT_SIZE_DEFAULT ) {
|
|
m_tf |= TF_FONT_POINT_SIZE;
|
|
m_iPointSize = pointSize;
|
|
// If using absolute point size, then relative font size must be default
|
|
m_iFontSize = GetDefaultFontSize();
|
|
m_tf &= ~TF_FONT_SIZE;
|
|
}
|
|
}
|
|
|
|
void CEditTextElement::SetScriptExtra( char* pScriptExtra ){
|
|
if ( m_pScriptExtra ) {
|
|
XP_FREE(m_pScriptExtra);
|
|
m_pScriptExtra = NULL;
|
|
}
|
|
if ( pScriptExtra ) {
|
|
m_pScriptExtra = XP_STRDUP(pScriptExtra);
|
|
}
|
|
}
|
|
|
|
void CEditTextElement::SetHREF( ED_LinkId id ){
|
|
if( m_href != ED_LINK_ID_NONE ){
|
|
m_href->Release();
|
|
}
|
|
m_href = id;
|
|
// Dont set HREF if we are a script
|
|
if( id != ED_LINK_ID_NONE && !EDT_IS_SCRIPT(m_tf) ){
|
|
id->AddRef();
|
|
m_tf |= TF_HREF;
|
|
}
|
|
else {
|
|
m_tf &= ~TF_HREF;
|
|
}
|
|
}
|
|
|
|
int CEditTextElement::GetFontSize(){
|
|
if( m_tf & TF_FONT_SIZE ){
|
|
return m_iFontSize;
|
|
}
|
|
else {
|
|
return GetDefaultFontSize();
|
|
}
|
|
}
|
|
|
|
char* CEditTextElement::GetFontFace(){
|
|
return m_pFace;
|
|
}
|
|
|
|
int16 CEditTextElement::GetFontWeight(){
|
|
return m_iWeight;
|
|
}
|
|
|
|
int16 CEditTextElement::GetFontPointSize(){
|
|
return m_iPointSize;
|
|
}
|
|
|
|
char* CEditTextElement::GetScriptExtra(){
|
|
return m_pScriptExtra;
|
|
}
|
|
|
|
//
|
|
// The the current text buffer but convert spaces to non breaking spaces where
|
|
// necessary so the file can be laid out properly.
|
|
//
|
|
// The rules for converting spaces into Non breaking spaces are as follows:
|
|
//
|
|
// X = character
|
|
// N = Non breaking space
|
|
// S = Space
|
|
// ^ = End of line
|
|
//
|
|
// 1 2 N
|
|
// ---------------------------------------
|
|
// Line Start N NS NNNS
|
|
// Middle XS XNS XNNNS
|
|
// End N^ XNN^ XNNN^
|
|
//
|
|
//
|
|
char* CEditTextElement::GetTextWithConvertedSpaces(){
|
|
|
|
// If regular get text doesn't do anything, neither should we.
|
|
if( GetText() == 0 ){
|
|
return 0;
|
|
}
|
|
|
|
// grab a working buffer to copy the data into
|
|
char *pBuf = edt_WorkBuf( GetLen()+1 );
|
|
|
|
// Out of memory
|
|
if( pBuf == 0 ) return 0;
|
|
|
|
CEditElement *pPrev = PreviousLeafInContainer();
|
|
CEditElement *pNext = LeafInContainerAfter();
|
|
|
|
XP_Bool bNextIsSpace, bNextIsEOL, bIsSpace, bAtBeginOfLine;
|
|
|
|
|
|
// if we are the beginning of a line
|
|
bAtBeginOfLine = ( pPrev == 0 || pPrev->CausesBreakAfter() );
|
|
|
|
char *pDest = pBuf;
|
|
const char *pSrc = GetText();
|
|
|
|
while( *pSrc ) {
|
|
|
|
bIsSpace = (*pSrc == ' ');
|
|
|
|
if( pSrc[1] != 0 ){
|
|
bNextIsEOL = FALSE;
|
|
bNextIsSpace = (pSrc[1] == ' ');
|
|
}
|
|
else if( pNext
|
|
&& pNext->IsText()
|
|
&& pNext->Text()->GetText()) {
|
|
bNextIsEOL = FALSE;
|
|
bNextIsSpace = ( pNext->Text()->GetText()[0] == ' ' );
|
|
}
|
|
else {
|
|
bNextIsEOL = TRUE;
|
|
bNextIsSpace = FALSE;
|
|
}
|
|
|
|
if( bAtBeginOfLine ){
|
|
if( bIsSpace ){
|
|
*pDest = (char) NON_BREAKING_SPACE;
|
|
}
|
|
else {
|
|
*pDest = *pSrc;
|
|
}
|
|
bAtBeginOfLine = FALSE;
|
|
}
|
|
else {
|
|
if( bIsSpace && ( bNextIsSpace || bNextIsEOL )){
|
|
*pDest = (char) NON_BREAKING_SPACE;
|
|
}
|
|
else {
|
|
*pDest = *pSrc;
|
|
}
|
|
}
|
|
|
|
pSrc++;
|
|
pDest++;
|
|
}
|
|
*pDest = 0;
|
|
return pBuf;
|
|
}
|
|
|
|
void CEditTextElement::ClearFormatting(){
|
|
if ( m_tf & TF_HREF) {
|
|
if ( m_href != ED_LINK_ID_NONE ) {
|
|
m_href->Release();
|
|
}
|
|
else {
|
|
XP_ASSERT(FALSE);
|
|
}
|
|
}
|
|
m_href = ED_LINK_ID_NONE;
|
|
m_tf = 0;
|
|
SetFontSize( GetDefaultFontSize() );
|
|
m_color = ED_Color::GetUndefined();
|
|
m_iWeight = ED_FONT_WEIGHT_NORMAL;
|
|
m_iPointSize = ED_FONT_POINT_SIZE_DEFAULT;
|
|
|
|
/* We don't preserve the script extra because the SCRIPT attribute is cleared.
|
|
*/
|
|
SetScriptExtra(NULL);
|
|
}
|
|
|
|
void CEditTextElement::SetData( EDT_CharacterData *pData ){
|
|
|
|
// Either Java Script type
|
|
ED_TextFormat tfJava = TF_SERVER | TF_SCRIPT | TF_STYLE;
|
|
|
|
// if we are setting server or script,
|
|
// then it is the only thing we need to do
|
|
if( (pData->mask & pData->values ) & tfJava ){
|
|
ClearFormatting();
|
|
#ifdef USE_SCRIPT
|
|
m_tf = (pData->mask & pData->values ) & tfJava;
|
|
return;
|
|
#else
|
|
XP_ASSERT(FALSE); // Front ends should not be setting these styles any more.
|
|
return;
|
|
#endif
|
|
}
|
|
#ifdef USE_SCRIPT
|
|
// If we are already a Java Script type,
|
|
// and we are NOT removing that type (mask bit is not set)
|
|
// then ignore all other attributes
|
|
if( (0 != (m_tf & TF_SERVER) &&
|
|
0 == (pData->mask & TF_SERVER)) ||
|
|
(0 != (m_tf & TF_STYLE) &&
|
|
0 == (pData->mask & TF_STYLE)) ||
|
|
(0 != (m_tf & TF_SCRIPT) &&
|
|
0 == (pData->mask & TF_SCRIPT)) ){
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if( pData->mask & TF_HREF ){
|
|
SetHREF( pData->linkId );
|
|
}
|
|
if( pData->mask & TF_FONT_COLOR ){
|
|
if( (pData->values & TF_FONT_COLOR) ){
|
|
SetColor( EDT_LO_COLOR(pData->pColor) );
|
|
}
|
|
else {
|
|
SetColor( ED_Color::GetUndefined() );
|
|
}
|
|
}
|
|
if( pData->mask & TF_FONT_SIZE ){
|
|
if( pData->values & TF_FONT_SIZE ){
|
|
SetFontSize( pData->iSize );
|
|
}
|
|
else {
|
|
SetFontSize( GetDefaultFontSize() );
|
|
XP_ASSERT( (m_tf & TF_FONT_SIZE) == 0 );
|
|
}
|
|
}
|
|
|
|
if( pData->mask & TF_FONT_FACE ){
|
|
if( (pData->values & TF_FONT_FACE) ){
|
|
SetFontFace( pData->pFontFace );
|
|
}
|
|
else {
|
|
SetFontFace( 0 );
|
|
}
|
|
}
|
|
|
|
if( pData->mask & TF_FONT_WEIGHT ){
|
|
if( (pData->values & TF_FONT_WEIGHT) ){
|
|
SetFontWeight( pData->iWeight );
|
|
}
|
|
else {
|
|
SetFontWeight( ED_FONT_WEIGHT_NORMAL );
|
|
}
|
|
}
|
|
|
|
if( pData->mask & TF_FONT_POINT_SIZE ){
|
|
if( (pData->values & TF_FONT_POINT_SIZE) ){
|
|
SetFontPointSize( pData->iPointSize );
|
|
}
|
|
else {
|
|
SetFontPointSize( ED_FONT_POINT_SIZE_DEFAULT );
|
|
}
|
|
}
|
|
|
|
// remove the elements we've already handled.
|
|
ED_TextFormat mask = pData->mask & ~( TF_HREF
|
|
| TF_FONT_SIZE
|
|
| TF_FONT_COLOR
|
|
| TF_FONT_FACE
|
|
| TF_FONT_WEIGHT
|
|
| TF_FONT_POINT_SIZE
|
|
#ifdef USE_SCRIPT
|
|
| TF_SCRIPT
|
|
| TF_SERVER
|
|
| TF_STYLE );
|
|
#else
|
|
);
|
|
#endif
|
|
|
|
ED_TextFormat values = pData->values & mask;
|
|
|
|
m_tf = (m_tf & ~mask) | values;
|
|
}
|
|
|
|
EDT_CharacterData* CEditTextElement::GetData(){
|
|
EDT_CharacterData *pData = EDT_NewCharacterData();
|
|
pData->mask = -1;
|
|
pData->values = m_tf;
|
|
pData->iSize = GetFontSize();
|
|
if( m_tf & TF_FONT_COLOR ){
|
|
pData->pColor = edt_MakeLoColor( GetColor() );
|
|
}
|
|
if( m_tf & TF_HREF ){
|
|
if( m_href != ED_LINK_ID_NONE ){
|
|
pData->pHREFData = m_href->GetData();
|
|
}
|
|
pData->linkId = m_href;
|
|
}
|
|
if( m_tf & TF_FONT_FACE ){
|
|
XP_ASSERT( m_pFace );
|
|
pData->pFontFace = XP_STRDUP( m_pFace );
|
|
}
|
|
if ( m_tf & TF_FONT_WEIGHT ){
|
|
pData->iWeight = m_iWeight;
|
|
}
|
|
if ( m_tf & TF_FONT_POINT_SIZE ){
|
|
pData->iPointSize = m_iPointSize;
|
|
}
|
|
return pData;
|
|
}
|
|
|
|
void CEditTextElement::MaskData( EDT_CharacterData*& pData ){
|
|
if( pData == 0 ){
|
|
pData = GetData();
|
|
return;
|
|
}
|
|
ED_TextFormat differences = pData->values ^ m_tf;
|
|
if( differences ){
|
|
// We are certain about all bits that are NOT different
|
|
pData->mask &= ~differences;
|
|
}
|
|
// If mask bit is still set, then we THINK the attribute
|
|
// is the same, but we don't know until we check the
|
|
// color, href size, or font face directly
|
|
if( (pData->mask & pData->values & m_tf & TF_FONT_COLOR)
|
|
&& EDT_LO_COLOR( pData->pColor ) != GetColor() ){
|
|
pData->mask &= ~TF_FONT_COLOR;
|
|
XP_FREE( pData->pColor );
|
|
pData->pColor = 0;
|
|
}
|
|
if( (pData->mask & pData->values & m_tf & TF_HREF)
|
|
&& (pData->linkId != m_href ) ){
|
|
pData->mask &= ~TF_HREF;
|
|
}
|
|
if( (pData->mask & pData->values & m_tf & TF_FONT_SIZE)
|
|
&& (pData->iSize != m_iFontSize) ){
|
|
pData->mask &= ~TF_FONT_SIZE;
|
|
}
|
|
if( (pData->mask & pData->values & m_tf & TF_FONT_POINT_SIZE)
|
|
&& (pData->iPointSize != m_iPointSize) ){
|
|
pData->mask &= ~TF_FONT_POINT_SIZE;
|
|
}
|
|
if( (pData->mask & pData->values & m_tf & TF_FONT_FACE) ){
|
|
// Both have font face -- see if it is different
|
|
char * pFontFace = GetFontFace();
|
|
|
|
// Note: String shouldn't be empty if bit is set!
|
|
XP_ASSERT(pData->pFontFace);
|
|
if( !pData->pFontFace && !pFontFace ){
|
|
// Both are empty so they are the same
|
|
return;
|
|
}
|
|
if( pData->pFontFace
|
|
&& (!pFontFace || XP_STRCMP(pData->pFontFace, pFontFace )) ){
|
|
// Font name is different
|
|
pData->mask &= ~TF_FONT_FACE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// insert a character into the text buffer. At some point we should make sure we
|
|
// don't insert two spaces together.
|
|
//
|
|
XP_Bool CEditTextElement::InsertChar( int iOffset, int newChar ){
|
|
char buffer[2];
|
|
buffer[0] = newChar;
|
|
buffer[1] = '\0';
|
|
return InsertChars(iOffset, buffer);
|
|
}
|
|
|
|
int32 CEditTextElement::InsertChars( int iOffset, char* pNewChars ){
|
|
// This is in bytes, not in characters.
|
|
// However, it assumes we've gotten whole characters.
|
|
int32 iNumBytes = pNewChars ? XP_STRLEN(pNewChars) : 0;
|
|
if ( iNumBytes <= 0 ) {
|
|
return 0;
|
|
}
|
|
// guarantee the buffer size
|
|
//
|
|
int32 iOldLen = GetLen();
|
|
int32 iSize = iOldLen + iNumBytes;
|
|
if( iSize + 1 >= m_textSize ){
|
|
int newSize = GROW_TEXT( iSize + 1 );
|
|
char *pNewBuf = (char*)XP_ALLOC( newSize );
|
|
if( pNewBuf == 0 ){
|
|
// out of memory, should probably throw.
|
|
return 0;
|
|
}
|
|
if( m_pText ){
|
|
XP_STRCPY( pNewBuf, m_pText );
|
|
XP_FREE( m_pText );
|
|
}
|
|
else {
|
|
pNewBuf[0] = 0;
|
|
}
|
|
m_pText = pNewBuf;
|
|
m_textSize = newSize;
|
|
}
|
|
|
|
//
|
|
// create an empty space in the string. We have to start from the end of
|
|
// the string and move backward. Move the null, too
|
|
//
|
|
int32 i;
|
|
for(i = iOldLen; i >= iOffset; i--){
|
|
m_pText[i+iNumBytes] = m_pText[i];
|
|
}
|
|
// Insert the new text into the string.
|
|
for(i = 0; i < iNumBytes; i++){
|
|
m_pText[iOffset+i] = pNewChars[i];
|
|
}
|
|
|
|
/* update the text block with the new text */
|
|
if ( m_pTextBlock != NULL ){
|
|
lo_ChangeText ( m_pTextBlock, GetTextWithConvertedSpaces() );
|
|
}
|
|
|
|
return iNumBytes;
|
|
}
|
|
|
|
void CEditTextElement::DeleteChar( MWContext *pContext, int iOffset ){
|
|
|
|
INTL_CharSetInfo csi = LO_GetDocumentCharacterSetInfo(pContext);
|
|
int i = iOffset;
|
|
char c = 1;
|
|
int clen;
|
|
|
|
clen = INTL_CharLen(INTL_GetCSIWinCSID(csi), (unsigned char *)m_pText+iOffset);
|
|
|
|
if( m_pText[iOffset] ){
|
|
while( c != 0 ){
|
|
m_pText[i] = c = m_pText[i+clen];
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
char* CEditTextElement::DebugFormat(){
|
|
static char buf[1024];
|
|
strcpy(buf, "( " );
|
|
int i;
|
|
if(m_tf & TF_BOLD) strcat(buf,"B ");
|
|
if(m_tf & TF_ITALIC) strcat(buf,"I ");
|
|
if(m_tf & TF_FIXED) strcat(buf,"TT ");
|
|
if(m_tf & TF_SUPER) strcat(buf,"SUP ");
|
|
if(m_tf & TF_SUB) strcat(buf,"SUB ");
|
|
if(m_tf & TF_STRIKEOUT) strcat(buf,"SO ");
|
|
if(m_tf & TF_UNDERLINE) strcat(buf,"U ");
|
|
if(m_tf & TF_BLINK) strcat(buf,"BL ");
|
|
#ifdef USE_SCRIPT
|
|
if(m_tf & TF_SERVER) strcat(buf,"SERVER ");
|
|
if(m_tf & TF_SCRIPT) strcat(buf,"SCRIPT ");
|
|
if(m_tf & TF_STYLE) strcat(buf,"STYLE ");
|
|
#endif
|
|
if(m_tf & TF_SPELL) strcat(buf,"SPELL ");
|
|
if(m_tf & TF_INLINEINPUT) strcat(buf,"INLINEINPUT ");
|
|
if(m_tf & TF_INLINEINPUTTHICK) strcat(buf,"INLINEINPUTTHICK ");
|
|
if(m_tf & TF_INLINEINPUTDOTTED) strcat(buf,"INLINEINPUTDOTTED ");
|
|
|
|
if(m_pScriptExtra){
|
|
strcat(buf, m_pScriptExtra);
|
|
strcat(buf," ");
|
|
}
|
|
|
|
if ( m_tf & TF_FONT_FACE && m_pFace ) {
|
|
strcat( buf, m_pFace );
|
|
strcat( buf, " ");
|
|
}
|
|
|
|
if ( m_tf & TF_FONT_WEIGHT ){
|
|
char sizeBuf[30];
|
|
PR_snprintf(sizeBuf, 30, "weight=%d ", m_iWeight );
|
|
strcat( buf, sizeBuf);
|
|
}
|
|
|
|
if ( m_tf & TF_FONT_POINT_SIZE ){
|
|
char sizeBuf[30];
|
|
PR_snprintf(sizeBuf, 30, "pointsize=%d ", m_iPointSize );
|
|
strcat( buf, sizeBuf);
|
|
}
|
|
|
|
if( m_tf & TF_FONT_COLOR ){
|
|
char color_str[8];
|
|
PR_snprintf(color_str, 8, "#%06lX ", GetColor().GetAsLong() );
|
|
strcat( buf, color_str );
|
|
}
|
|
|
|
if( m_tf & TF_FONT_SIZE ){
|
|
i = XP_STRLEN(buf);
|
|
buf[i] = (GetFontSize() < 3 ? '-': '+');
|
|
buf[i+1] = '0' + abs(GetFontSize() - 3);
|
|
buf[i+2] = ' ';
|
|
buf[i+3] = 0;
|
|
}
|
|
|
|
if( m_tf & TF_HREF ){
|
|
strcat( buf, m_href->hrefStr );
|
|
}
|
|
|
|
strcat(buf, ") " );
|
|
return buf;
|
|
}
|
|
|
|
//
|
|
// Create a list of tags that represents the character formatting for this
|
|
// text element.
|
|
//
|
|
void CEditTextElement::FormatOpenTags(PA_Tag*& pStart, PA_Tag*& pEnd){
|
|
|
|
|
|
if(m_tf & TF_BOLD){ edt_AddTag( pStart, pEnd, P_BOLD, FALSE ); }
|
|
if(m_tf & TF_ITALIC){ edt_AddTag( pStart, pEnd, P_ITALIC, FALSE ); }
|
|
if(m_tf & TF_FIXED){ edt_AddTag( pStart, pEnd, P_FIXED, FALSE ); }
|
|
if(m_tf & TF_SUPER){ edt_AddTag( pStart,pEnd, P_SUPER, FALSE ); }
|
|
if(m_tf & TF_SUB){ edt_AddTag( pStart,pEnd, P_SUB, FALSE ); }
|
|
if(m_tf & TF_NOBREAK){ edt_AddTag( pStart,pEnd, P_NOBREAK, FALSE ); }
|
|
if(m_tf & TF_STRIKEOUT){ edt_AddTag( pStart,pEnd, P_STRIKEOUT, FALSE ); }
|
|
if(m_tf & TF_UNDERLINE){ edt_AddTag( pStart,pEnd, P_UNDERLINE, FALSE ); }
|
|
if(m_tf & TF_BLINK){ edt_AddTag( pStart,pEnd, P_BLINK, FALSE ); }
|
|
if(m_tf & TF_SPELL){ edt_AddTag( pStart,pEnd, P_SPELL, FALSE ); }
|
|
if(m_tf & TF_INLINEINPUT){ edt_AddTag( pStart,pEnd, P_INLINEINPUT, FALSE ); }
|
|
if(m_tf & TF_INLINEINPUTTHICK){ edt_AddTag( pStart,pEnd, P_INLINEINPUTTHICK, FALSE ); }
|
|
if(m_tf & TF_INLINEINPUTDOTTED){ edt_AddTag( pStart,pEnd, P_INLINEINPUTDOTTED, FALSE ); }
|
|
|
|
// Face and color have to come before size for old browsers.
|
|
if( m_tf & TF_FONT_FACE ){
|
|
char buf[200];
|
|
XP_STRCPY( buf, " FACE=\"" );
|
|
strcat( buf, m_pFace );
|
|
strcat( buf, "\">" );
|
|
edt_AddTag( pStart,pEnd, P_FONT, FALSE, buf );
|
|
}
|
|
|
|
if ( m_tf & TF_FONT_WEIGHT ) {
|
|
char buf[20];
|
|
PR_snprintf(buf, 20, " FONT-WEIGHT=%d>", m_iWeight);
|
|
edt_AddTag( pStart,pEnd, P_FONT, FALSE, buf );
|
|
}
|
|
|
|
if ( m_tf & TF_FONT_POINT_SIZE ) {
|
|
char buf[20];
|
|
PR_snprintf(buf, 20, " POINT-SIZE=%d>", m_iPointSize);
|
|
edt_AddTag( pStart,pEnd, P_FONT, FALSE, buf );
|
|
}
|
|
|
|
if( GetColor().IsDefined() ){
|
|
char buf[20];
|
|
PR_snprintf(buf, 20, "COLOR=\"#%06lX\">", GetColor().GetAsLong() );
|
|
edt_AddTag( pStart,pEnd, P_FONT, FALSE, buf );
|
|
}
|
|
|
|
if( m_tf & TF_FONT_SIZE ){
|
|
char buf[20];
|
|
XP_STRCPY( buf, " SIZE=" );
|
|
int i = XP_STRLEN(buf);
|
|
buf[i] = (GetFontSize() < 3 ? '-': '+');
|
|
buf[i+1] = '0' + abs(GetFontSize() - 3);
|
|
buf[i+2] = '>';
|
|
buf[i+3] = 0;
|
|
edt_AddTag( pStart,pEnd, P_FONT, FALSE, buf );
|
|
}
|
|
|
|
if( m_tf & TF_HREF){
|
|
char *pBuf = PR_smprintf("HREF=%s %s\">", edt_MakeParamString(m_href->hrefStr),
|
|
(m_href->pExtra ? m_href->pExtra : "") );
|
|
edt_AddTag( pStart,pEnd, P_ANCHOR, FALSE, pBuf );
|
|
free( pBuf );
|
|
}
|
|
|
|
// if EVAL, hack the character attributes...
|
|
#ifdef USE_SCRIPT
|
|
if(m_tf & TF_SERVER){
|
|
edt_AddTag( pStart, pEnd, P_SERVER, FALSE );
|
|
return;
|
|
}
|
|
|
|
if(m_tf & TF_SCRIPT){
|
|
edt_AddTag( pStart, pEnd, P_SCRIPT, FALSE );
|
|
return;
|
|
}
|
|
|
|
if(m_tf & TF_STYLE){
|
|
edt_AddTag( pStart, pEnd, P_STYLE, FALSE );
|
|
return;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CEditTextElement::FormatTransitionTags(CEditTextElement* /* pNext */,
|
|
PA_Tag*& /* pStart */, PA_Tag*& /* pEnd */){
|
|
}
|
|
|
|
void CEditTextElement::FormatCloseTags(PA_Tag*& pStart, PA_Tag*& pEnd){
|
|
|
|
// WARNING: order here is real important, Open and Close must match
|
|
// exactly.
|
|
|
|
// if EVAL must be last
|
|
#ifdef USE_SCRIPT
|
|
if(m_tf & TF_STYLE){
|
|
edt_AddTag( pStart, pEnd, P_STYLE, TRUE );
|
|
}
|
|
|
|
if(m_tf & TF_SCRIPT){
|
|
edt_AddTag( pStart, pEnd, P_SCRIPT, TRUE );
|
|
}
|
|
|
|
if(m_tf & TF_SERVER){
|
|
edt_AddTag( pStart, pEnd, P_SERVER, TRUE );
|
|
}
|
|
#endif
|
|
|
|
//if(m_href){ edt_AddTag( pStart,pEnd, P_ITALIC, FALSE ); }
|
|
if( m_tf & TF_HREF ){ edt_AddTag( pStart,pEnd, P_ANCHOR, TRUE );}
|
|
if( m_tf & TF_FONT_SIZE ){edt_AddTag( pStart,pEnd, P_FONT, TRUE );}
|
|
if( GetColor().IsDefined() ){edt_AddTag( pStart,pEnd, P_FONT, TRUE );}
|
|
if( m_tf & TF_FONT_POINT_SIZE ) edt_AddTag( pStart,pEnd, P_FONT, TRUE );
|
|
if( m_tf & TF_FONT_WEIGHT ) edt_AddTag( pStart,pEnd, P_FONT, TRUE );
|
|
if( m_tf & TF_FONT_FACE ) edt_AddTag( pStart,pEnd, P_FONT, TRUE );
|
|
|
|
if(m_tf & TF_INLINEINPUTDOTTED ){ edt_AddTag( pStart,pEnd, P_INLINEINPUTDOTTED, TRUE); }
|
|
if(m_tf & TF_INLINEINPUTTHICK ){ edt_AddTag( pStart,pEnd, P_INLINEINPUTTHICK, TRUE); }
|
|
if(m_tf & TF_INLINEINPUT ){ edt_AddTag( pStart,pEnd, P_INLINEINPUT, TRUE); }
|
|
if(m_tf & TF_SPELL ){ edt_AddTag( pStart,pEnd, P_SPELL, TRUE); }
|
|
if(m_tf & TF_BLINK ){ edt_AddTag( pStart,pEnd, P_BLINK, TRUE); }
|
|
if(m_tf & TF_STRIKEOUT ){ edt_AddTag( pStart,pEnd, P_STRIKEOUT, TRUE); }
|
|
if(m_tf & TF_UNDERLINE ){ edt_AddTag( pStart,pEnd, P_UNDERLINE, TRUE); }
|
|
if(m_tf & TF_NOBREAK ){ edt_AddTag( pStart,pEnd, P_NOBREAK, TRUE); }
|
|
if(m_tf & TF_SUB ){ edt_AddTag( pStart,pEnd, P_SUB, TRUE); }
|
|
if(m_tf & TF_SUPER ){ edt_AddTag( pStart,pEnd, P_SUPER, TRUE); }
|
|
if(m_tf & TF_FIXED ){ edt_AddTag( pStart, pEnd, P_FIXED, TRUE); }
|
|
if(m_tf & TF_ITALIC ){ edt_AddTag( pStart, pEnd, P_ITALIC, TRUE); }
|
|
if(m_tf & TF_BOLD ){ edt_AddTag( pStart, pEnd, P_BOLD, TRUE); }
|
|
|
|
|
|
}
|
|
|
|
//
|
|
// Create a text tag. We are going to be feeding these tags to the layout
|
|
// engine. The layout element this edit element points to is going to change.
|
|
//
|
|
PA_Tag* CEditTextElement::TagOpen( int iEditOffset ){
|
|
// LTNOTE: I think that we only want to do this if we are the first text
|
|
// in the container, other wise, we don't do anything.
|
|
PA_Tag *pStart = 0;
|
|
PA_Tag *pEnd = 0;
|
|
XP_Bool bFirst = IsFirstInContainer();
|
|
|
|
if( GetLen() == 0 && (!bFirst || (bFirst && LeafInContainerAfter() != 0))){
|
|
return 0;
|
|
}
|
|
|
|
PA_Tag *pTag = XP_NEW( PA_Tag );
|
|
XP_BZERO( pTag, sizeof( PA_Tag ) );
|
|
|
|
FormatOpenTags( pStart, pEnd );
|
|
|
|
if( pStart == 0 ){
|
|
pStart = pTag;
|
|
}
|
|
if( pEnd ){
|
|
pEnd->next = pTag;
|
|
}
|
|
pEnd = pTag;
|
|
|
|
if(GetLen() == 0){
|
|
// As of 3.0b5, a non-breaking space always caused a memory leak when
|
|
// it was laid out. See bug 23404
|
|
// So use a different character here. The character is
|
|
// removed by code in CEditBuffer::Relayout
|
|
#ifdef USE_PERIOD_FOR_BLANK_LAYOUT
|
|
SetTagData(pTag, ".");
|
|
#else
|
|
SetTagData(pTag, NON_BREAKING_SPACE_STRING);
|
|
#endif
|
|
//SetTagData(pTag, "");
|
|
}
|
|
else {
|
|
SetTagData(pTag, GetTextWithConvertedSpaces()+iEditOffset);
|
|
}
|
|
if( iEditOffset==0 ){
|
|
m_pFirstLayoutElement = 0;
|
|
}
|
|
|
|
FormatCloseTags( pStart, pEnd );
|
|
|
|
return pStart;
|
|
}
|
|
|
|
XP_Bool CEditTextElement::GetLOTextAndOffset( ElementOffset iEditOffset, XP_Bool bStickyAfter, LO_TextStruct*& pRetText,
|
|
int& iLayoutOffset ){
|
|
|
|
LO_TextStruct *pText;
|
|
LO_Element* pLoElement = m_pFirstLayoutElement;
|
|
XP_ASSERT(!pLoElement || pLoElement->lo_any.edit_offset == 0);
|
|
intn len = GetLen();
|
|
|
|
while(pLoElement != 0){
|
|
if( pLoElement->type == LO_TEXT ){
|
|
// if we have scanned past our edit element, we can't find the
|
|
// insert point.
|
|
pText = &(pLoElement->lo_text);
|
|
if( pText->edit_element != this){
|
|
/* assert(0)
|
|
*/
|
|
return FALSE;
|
|
}
|
|
|
|
int16 textLen = pText->text_len;
|
|
int32 loEnd = pText->edit_offset + textLen;
|
|
|
|
//
|
|
// See if we are at the dreaded lopped of the last space case.
|
|
//
|
|
if( loEnd+1 == iEditOffset ){
|
|
// ok, we've scaned past the end of the Element
|
|
if( len == iEditOffset && m_pText[iEditOffset-1] == ' '){
|
|
// we wraped to the next element. Return the next Text Element.
|
|
CEditElement *pNext = FindNextElement(&CEditElement::FindText,0);
|
|
if( pNext ){
|
|
return pNext->Text()->GetLOTextAndOffset( 0, FALSE, pRetText, iLayoutOffset );
|
|
}
|
|
|
|
// we should have all these cases handled now.
|
|
|
|
// jhp Nope. (a least as of Beta 2) If you have a very narrow document,
|
|
// and you
|
|
// type in two lines of text, and you're at the end of the
|
|
// second line, and you type a space, and the
|
|
// space causes a line wrap, then you end up here.
|
|
// XP_ASSERT(FALSE);
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if( loEnd > iEditOffset || (loEnd == iEditOffset
|
|
&& (!bStickyAfter || len == iEditOffset || textLen == 0 ) ) ){
|
|
// we've found the right element, return the information.
|
|
iLayoutOffset = iEditOffset - pText->edit_offset;
|
|
pRetText = pText;
|
|
return TRUE;
|
|
}
|
|
|
|
}
|
|
pLoElement = pLoElement->lo_any.next;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
LO_TextStruct* CEditTextElement::GetLOText( int iEditOffset ){
|
|
|
|
LO_TextStruct *pText;
|
|
LO_Element* pLoElement = m_pFirstLayoutElement;
|
|
|
|
while(pLoElement != 0){
|
|
if( pLoElement->type == LO_TEXT ){
|
|
// if we have scanned past our edit element, we can't find the
|
|
// insert point.
|
|
pText = &(pLoElement->lo_text);
|
|
if( pText->edit_element != this){
|
|
return 0;
|
|
}
|
|
if( pText->edit_offset > iEditOffset ){
|
|
return 0;
|
|
}
|
|
|
|
if( pText->edit_offset + pText->text_len >= iEditOffset ){
|
|
// we've found the right element, return the information.
|
|
return pText;
|
|
}
|
|
|
|
}
|
|
pLoElement = pLoElement->lo_any.next;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
LO_TextBlock* CEditTextElement::GetTextBlock()
|
|
{
|
|
return m_pTextBlock;
|
|
}
|
|
|
|
|
|
XP_Bool CEditTextElement::CanReflow() {
|
|
// we can only reflow if we have a text block
|
|
return m_pTextBlock != NULL;
|
|
}
|
|
|
|
void CEditTextElement::PrintTagOpen( CPrintState *pPrintState, TagType t,
|
|
ED_TextFormat tf, char* pExtra ){
|
|
if ( ! pExtra ) pExtra = ">";
|
|
pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "<%s%s",
|
|
EDT_TagString(t), pExtra );
|
|
pPrintState->m_formatStack.Push(tf);
|
|
pPrintState->m_elementStack.Push(this);
|
|
}
|
|
|
|
|
|
void CEditTextElement::PrintFormatDifference( CPrintState *ps,
|
|
ED_TextFormat bitsDifferent ){
|
|
if(bitsDifferent & TF_BOLD){ PrintTagOpen( ps, P_BOLD, TF_BOLD ); }
|
|
if(bitsDifferent & TF_ITALIC){ PrintTagOpen( ps, P_ITALIC, TF_ITALIC ); }
|
|
if(bitsDifferent & TF_FIXED){ PrintTagOpen( ps, P_FIXED, TF_FIXED ); }
|
|
if(bitsDifferent & TF_SUPER){ PrintTagOpen( ps, P_SUPER, TF_SUPER ); }
|
|
if(bitsDifferent & TF_SUB){ PrintTagOpen( ps, P_SUB, TF_SUB ); }
|
|
if(bitsDifferent & TF_NOBREAK){ PrintTagOpen( ps, P_NOBREAK, TF_NOBREAK ); }
|
|
if(bitsDifferent & TF_STRIKEOUT){ PrintTagOpen( ps, P_STRIKEOUT, TF_STRIKEOUT ); }
|
|
if(bitsDifferent & TF_UNDERLINE){ PrintTagOpen( ps, P_UNDERLINE, TF_UNDERLINE ); }
|
|
if(bitsDifferent & TF_BLINK){ PrintTagOpen( ps, P_BLINK, TF_BLINK ); }
|
|
if(bitsDifferent & TF_SPELL){ /*do nothing*/ }
|
|
if(bitsDifferent & TF_INLINEINPUT){ /*do nothing*/ }
|
|
if(bitsDifferent & TF_INLINEINPUTTHICK){ /*do nothing*/ }
|
|
if(bitsDifferent & TF_INLINEINPUTDOTTED){ /*do nothing*/ }
|
|
|
|
// Face and color have to preceed FONT_SIZE, or else old browsers will
|
|
// reset the font_size to zero when they encounter the unknown tags.
|
|
|
|
if( bitsDifferent & TF_FONT_FACE ){
|
|
ps->m_iCharPos += ps->m_pOut->Printf( "<FONT FACE=\"%s\">", m_pFace);
|
|
ps->m_formatStack.Push(TF_FONT_FACE);
|
|
ps->m_elementStack.Push(this);
|
|
}
|
|
|
|
if( bitsDifferent & TF_FONT_WEIGHT ){
|
|
ps->m_iCharPos += ps->m_pOut->Printf( "<FONT FONT-WEIGHT=\"%d\">", m_iWeight);
|
|
ps->m_formatStack.Push(TF_FONT_WEIGHT);
|
|
ps->m_elementStack.Push(this);
|
|
}
|
|
|
|
if( bitsDifferent & TF_FONT_POINT_SIZE ){
|
|
ps->m_iCharPos += ps->m_pOut->Printf( "<FONT POINT-SIZE=\"%d\">", m_iPointSize);
|
|
ps->m_formatStack.Push(TF_FONT_POINT_SIZE);
|
|
ps->m_elementStack.Push(this);
|
|
}
|
|
|
|
if( bitsDifferent & TF_FONT_COLOR ){
|
|
ps->m_iCharPos += ps->m_pOut->Printf( "<FONT COLOR=\"#%06lX\">",
|
|
GetColor().GetAsLong() );
|
|
ps->m_formatStack.Push(TF_FONT_COLOR);
|
|
ps->m_elementStack.Push(this);
|
|
}
|
|
|
|
if( bitsDifferent & TF_FONT_SIZE ){
|
|
char buf[4];
|
|
buf[0] = (GetFontSize() < 3 ? '-': '+');
|
|
buf[1] = '0' + abs(GetFontSize() - 3);
|
|
buf[2] = 0;
|
|
ps->m_iCharPos += ps->m_pOut->Printf( "<FONT SIZE=%s>", buf);
|
|
ps->m_formatStack.Push(TF_FONT_SIZE);
|
|
ps->m_elementStack.Push(this);
|
|
}
|
|
|
|
if( bitsDifferent & TF_HREF){
|
|
// doesn't use the output routine because HREF can be large.
|
|
char *pS1 = "";
|
|
char *pS2 = "";
|
|
if( m_href->pExtra ){
|
|
pS1 = " ";
|
|
pS2 = m_href->pExtra;
|
|
}
|
|
ps->m_iCharPos += ps->m_pOut->Printf(
|
|
"<A HREF=%s%s%s>", edt_MakeParamString(m_href->hrefStr), pS1, pS2 );
|
|
|
|
ps->m_formatStack.Push(TF_HREF);
|
|
ps->m_elementStack.Push(this);
|
|
}
|
|
|
|
#ifdef USE_SCRIPT
|
|
if(bitsDifferent & TF_STYLE){
|
|
PrintTagOpen( ps, P_STYLE, TF_STYLE, GetScriptExtra() ); }
|
|
if(bitsDifferent & TF_SCRIPT){
|
|
PrintTagOpen( ps, P_SCRIPT, TF_SCRIPT, GetScriptExtra() ); }
|
|
if(bitsDifferent & TF_SERVER){ PrintTagOpen( ps, P_SERVER, TF_SERVER, GetScriptExtra() ); }
|
|
#endif
|
|
}
|
|
|
|
void CEditTextElement::PrintFormat( CPrintState *ps,
|
|
CEditTextElement *pFirst,
|
|
ED_TextFormat mask ){
|
|
ED_TextFormat bitsCommon = 0;
|
|
ED_TextFormat bitsDifferent = 0;
|
|
|
|
ComputeDifference(pFirst, mask, bitsCommon, bitsDifferent);
|
|
|
|
CEditTextElement *pNext = (CEditTextElement*) TextInContainerAfter();
|
|
|
|
if( bitsCommon ){
|
|
// While we're in a run of the same style, there's nothing to do.
|
|
// Avoid unnescessary recursion so we don't blow the stack on Mac or Win16
|
|
while ( pNext && SameAttributes(pNext) ){
|
|
pNext = (CEditTextElement*) pNext->TextInContainerAfter();
|
|
}
|
|
if( pNext ){
|
|
pNext->PrintFormat( ps, pFirst, bitsCommon );
|
|
}
|
|
else {
|
|
// we hit the end, so we have to open everything
|
|
pFirst->PrintFormatDifference( ps, bitsCommon);
|
|
}
|
|
}
|
|
if( bitsDifferent ){
|
|
pFirst->PrintFormatDifference( ps, bitsDifferent );
|
|
}
|
|
}
|
|
|
|
XP_Bool CEditTextElement::SameFormat(CEditTextElement *pCompare)
|
|
{
|
|
XP_Bool bSame = SameAttributes(pCompare);
|
|
if( bSame )
|
|
{
|
|
bSame = (m_tf == pCompare->m_tf) ? TRUE : FALSE;
|
|
}
|
|
return bSame;
|
|
}
|
|
|
|
XP_Bool CEditTextElement::SameAttributes(CEditTextElement *pCompare){
|
|
ED_TextFormat bitsCommon;
|
|
ED_TextFormat bitsDifferent;
|
|
ComputeDifference(pCompare, -1, bitsCommon, bitsDifferent);
|
|
return ( bitsCommon == m_tf);
|
|
}
|
|
|
|
void CEditTextElement::ComputeDifference(CEditTextElement *pFirst,
|
|
ED_TextFormat mask, ED_TextFormat& bitsCommon, ED_TextFormat& bitsDifferent){
|
|
bitsCommon = m_tf & mask;
|
|
bitsDifferent = mask & ~bitsCommon;
|
|
|
|
if( (bitsCommon & TF_FONT_SIZE)&& (GetFontSize() != pFirst->GetFontSize())){
|
|
bitsCommon &= ~TF_FONT_SIZE;
|
|
bitsDifferent |= TF_FONT_SIZE;
|
|
}
|
|
|
|
if( (bitsCommon & TF_FONT_COLOR)&& (GetColor() != pFirst->GetColor())){
|
|
bitsCommon &= ~TF_FONT_COLOR;
|
|
bitsDifferent |= TF_FONT_COLOR;
|
|
}
|
|
|
|
if( (bitsCommon & TF_FONT_POINT_SIZE)&& (GetFontPointSize() != pFirst->GetFontPointSize())){
|
|
bitsCommon &= ~TF_FONT_POINT_SIZE;
|
|
bitsDifferent |= TF_FONT_POINT_SIZE;
|
|
}
|
|
|
|
if( (bitsCommon & TF_FONT_WEIGHT)&& (GetFontWeight() != pFirst->GetFontWeight())){
|
|
bitsCommon &= ~TF_FONT_WEIGHT;
|
|
bitsDifferent |= TF_FONT_WEIGHT;
|
|
}
|
|
|
|
//CLM: These pointers were sometimes NULL
|
|
char * pFontFace = GetFontFace();
|
|
char * pFirstFontFace = pFirst->GetFontFace();
|
|
// Assure that we have strings to compare for font face
|
|
if( !pFontFace ){
|
|
pFontFace = " ";
|
|
}
|
|
if( !pFirstFontFace ){
|
|
pFirstFontFace = " ";
|
|
}
|
|
|
|
if( (bitsCommon & TF_FONT_FACE) && XP_STRCMP(pFontFace,pFirstFontFace) != 0 ){
|
|
bitsCommon &= ~TF_FONT_FACE;
|
|
bitsDifferent |= TF_FONT_FACE;
|
|
}
|
|
if( bitsCommon & TF_FONT_FACE ) {
|
|
char* a = GetFontFace();
|
|
char* b = pFirst->GetFontFace();
|
|
XP_Bool bA = a != NULL;
|
|
XP_Bool bB = b != NULL;
|
|
if ((bA ^ bB) ||
|
|
(bA && bB && XP_STRCMP(a, b) != 0)) {
|
|
bitsCommon &= ~TF_FONT_FACE;
|
|
bitsDifferent |= TF_FONT_FACE;
|
|
}
|
|
}
|
|
if( (bitsCommon & TF_HREF)&& (GetHREF() != pFirst->GetHREF())){
|
|
bitsCommon &= ~TF_HREF;
|
|
bitsDifferent |= TF_HREF;
|
|
}
|
|
}
|
|
|
|
void CEditTextElement::PrintTagClose( CPrintState *ps, TagType t ){
|
|
ps->m_iCharPos += ps->m_pOut->Printf( "</%s>",
|
|
EDT_TagString(t) );
|
|
}
|
|
|
|
void CEditTextElement::PrintPopFormat( CPrintState *ps, int iStackTop ){
|
|
|
|
while( ps->m_formatStack.m_iTop >= iStackTop ){
|
|
|
|
ED_TextFormat tf = (ED_TextFormat) ps->m_formatStack.Pop();
|
|
ps->m_elementStack.Pop();
|
|
#ifdef USE_SCRIPT
|
|
if( tf & TF_STYLE){ PrintTagClose( ps, P_STYLE ); }
|
|
if( tf & TF_SCRIPT){ PrintTagClose( ps, P_SCRIPT ); }
|
|
if( tf & TF_SERVER){ PrintTagClose( ps, P_SERVER ); }
|
|
#endif
|
|
if( tf & TF_BOLD){ PrintTagClose( ps, P_BOLD ); }
|
|
if( tf & TF_ITALIC){ PrintTagClose( ps, P_ITALIC ); }
|
|
if( tf & TF_FIXED){ PrintTagClose( ps, P_FIXED ); }
|
|
if( tf & TF_SUPER){ PrintTagClose( ps, P_SUPER ); }
|
|
if( tf & TF_SUB){ PrintTagClose( ps, P_SUB ); }
|
|
if( tf & TF_NOBREAK){ PrintTagClose( ps, P_NOBREAK ); }
|
|
if( tf & TF_STRIKEOUT){ PrintTagClose( ps, P_STRIKEOUT ); }
|
|
if( tf & TF_UNDERLINE){ PrintTagClose( ps, P_UNDERLINE ); }
|
|
if( tf & TF_BLINK){ PrintTagClose( ps, P_BLINK ); }
|
|
if( tf & TF_SPELL){ /* do nothing */ }
|
|
if( tf & TF_INLINEINPUT){ /* do nothing */ }
|
|
if( tf & TF_INLINEINPUTTHICK){ /* do nothing */ }
|
|
if( tf & TF_INLINEINPUTDOTTED){ /* do nothing */ }
|
|
if( tf & TF_FONT_SIZE){ PrintTagClose( ps, P_FONT ); }
|
|
if( tf & TF_FONT_COLOR){ PrintTagClose( ps, P_FONT ); }
|
|
if( tf & TF_FONT_POINT_SIZE){ PrintTagClose( ps, P_FONT ); }
|
|
if( tf & TF_FONT_WEIGHT){ PrintTagClose( ps, P_FONT ); }
|
|
if( tf & TF_FONT_FACE){ PrintTagClose( ps, P_FONT ); }
|
|
if( tf & TF_HREF){ PrintTagClose( ps, P_ANCHOR ); }
|
|
}
|
|
}
|
|
|
|
ED_TextFormat CEditTextElement::PrintFormatClose( CPrintState *ps ){
|
|
XP_Bool bDiff = FALSE;
|
|
ED_TextFormat bitsNeedFormat = m_tf ;
|
|
|
|
if( ps->m_formatStack.IsEmpty()){
|
|
return bitsNeedFormat;
|
|
}
|
|
int i = -1;
|
|
while( !bDiff && ++i <= ps->m_formatStack.m_iTop ){
|
|
ED_TextFormat tf = (ED_TextFormat) ps->m_formatStack[i];
|
|
if( m_tf & tf ){
|
|
if( tf == TF_FONT_COLOR
|
|
&& GetColor() != ps->m_elementStack[i]->GetColor()){
|
|
bDiff = TRUE;
|
|
}
|
|
else if( tf == TF_FONT_SIZE
|
|
&& GetFontSize() != ps->m_elementStack[i]->GetFontSize()){
|
|
bDiff = TRUE;
|
|
}
|
|
else if( tf == TF_FONT_POINT_SIZE
|
|
&& GetFontPointSize() != ps->m_elementStack[i]->GetFontPointSize()){
|
|
bDiff = TRUE;
|
|
}
|
|
else if( tf == TF_FONT_WEIGHT
|
|
&& GetFontWeight() != ps->m_elementStack[i]->GetFontWeight()){
|
|
bDiff = TRUE;
|
|
}
|
|
else if( tf == TF_FONT_FACE
|
|
&& XP_STRCMP(GetFontFace(), ps->m_elementStack[i]->GetFontFace()) != 0){
|
|
bDiff = TRUE;
|
|
}
|
|
else if( tf == TF_HREF
|
|
&& GetHREF() != ps->m_elementStack[i]->GetHREF() ){
|
|
bDiff = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
bDiff = TRUE;
|
|
}
|
|
|
|
// if the stuff is on the stack, then it doesn't need to be formatted
|
|
if( !bDiff ){
|
|
bitsNeedFormat &= ~tf;
|
|
}
|
|
}
|
|
PrintPopFormat( ps, i );
|
|
return bitsNeedFormat;
|
|
|
|
}
|
|
|
|
// for use within
|
|
void CEditTextElement::PrintWithEscapes( CPrintState *ps, XP_Bool bTrimTrailingSpaces ){
|
|
char *p = GetTextWithConvertedSpaces();
|
|
int csid = INTL_DefaultWinCharSetID(ps->m_pBuffer->m_pContext);
|
|
|
|
// Trim the last non-breaking spaces at the end of a
|
|
// top-level paragraph. The spaces just irritate users,
|
|
// especially in HTML mail.
|
|
// (But don't trim the last nbsp if it's the only thing in the paragraph, or
|
|
// else layout will ignore the paragraph.)
|
|
// And don't trim them in tables, where they're used to affect column width.
|
|
|
|
if ( bTrimTrailingSpaces && p && ! LeafInContainerAfter() && ! GetSubDocSkipRoot() ) {
|
|
/* find last NBSP by going forward char-by-char */
|
|
char *s, *last_nbsp, *end;
|
|
s = last_nbsp = p;
|
|
end = p + XP_STRLEN(p);
|
|
while ((s <= end) && (*s)) {
|
|
/* if not nbsp then advance both pointers */
|
|
if (*s != NON_BREAKING_SPACE)
|
|
last_nbsp = s = INTL_NextChar(csid, s);
|
|
else
|
|
s = INTL_NextChar(csid, s);
|
|
}
|
|
if (last_nbsp <= end){
|
|
// Don't trim last nbsp if it's the only thing in the paragraph.
|
|
if ( p == last_nbsp && IsFirstInContainer() && last_nbsp < end ){
|
|
last_nbsp++;
|
|
}
|
|
*last_nbsp = '\0'; /* points to last nbsp or end of string */
|
|
}
|
|
}
|
|
if( p == 0 ){
|
|
#ifndef EDT_DDT
|
|
p = NON_BREAKING_SPACE_STRING;
|
|
#endif
|
|
}
|
|
edt_PrintWithEscapes( ps, p, !InFormattedText() );
|
|
}
|
|
|
|
void CEditTextElement::PrintLiteral( CPrintState *ps ){
|
|
int iLen = GetLen();
|
|
ps->m_pOut->Write( GetText(), iLen );
|
|
ps->m_iCharPos += iLen;
|
|
}
|
|
|
|
void CEditTextElement::PrintOpen( CPrintState *ps){
|
|
ED_TextFormat bitsToFormat = PrintFormatClose( ps );
|
|
|
|
CEditTextElement *pNext = (CEditTextElement*) TextInContainerAfter();
|
|
if( pNext ){
|
|
pNext->PrintFormat( ps, this, bitsToFormat );
|
|
}
|
|
else {
|
|
PrintFormatDifference( ps, bitsToFormat );
|
|
}
|
|
|
|
if ( ps->ShouldPrintSelectionComments(this) ){
|
|
// Up to three pieces
|
|
int32 index = 0;
|
|
if ( ps->ShouldPrintSelectionComment(this, FALSE) ){
|
|
index = ps->m_selection.m_start.m_iPos;
|
|
if ( 0 < index ) {
|
|
PrintRange(ps, 0, index);
|
|
}
|
|
ps->PrintSelectionComment(FALSE, ps->m_selection.m_start.m_bStickyAfter);
|
|
}
|
|
if ( ps->ShouldPrintSelectionComment(this, TRUE) ){
|
|
int32 oldIndex = index;
|
|
index = ps->m_selection.m_end.m_iPos;
|
|
if ( oldIndex < index ) {
|
|
PrintRange(ps, oldIndex, index);
|
|
}
|
|
ps->PrintSelectionComment(TRUE, ps->m_selection.m_end.m_bStickyAfter);
|
|
}
|
|
if ( index < GetLen() ){
|
|
PrintRange(ps, index, GetLen());
|
|
}
|
|
}
|
|
else {
|
|
PrintOpen2(ps, TRUE);
|
|
}
|
|
|
|
if( pNext == 0 ) {
|
|
PrintPopFormat( ps, 0 );
|
|
}
|
|
}
|
|
|
|
void CEditTextElement::PrintRange( CPrintState *ps, int32 start, int32 end ){
|
|
// We modify m_pText in place and then call PrintOpen2 to do the
|
|
// actual printing. (And then we restore m_pText to its original
|
|
// condition.)
|
|
//
|
|
int32 len = GetLen();
|
|
if ( start == 0 && end == len ){
|
|
PrintOpen2(ps, FALSE);
|
|
}
|
|
else {
|
|
if ( end > len ) {
|
|
// This is getting annoying - XP_TRACE instead,
|
|
// since we survive it just fine
|
|
XP_TRACE(("PrintRange error: end = %d len = %d\n", end, len));
|
|
//XP_ASSERT(FALSE);
|
|
#ifdef DEBUG_akkana
|
|
printf("end = %d len = %d\n", end, len);
|
|
#endif
|
|
end = len;
|
|
}
|
|
if ( start < 0 ) {
|
|
XP_ASSERT(FALSE);
|
|
start = 0;
|
|
}
|
|
if ( start < end && m_pText ) {
|
|
char* pText = m_pText;
|
|
char save = pText[end];
|
|
pText[end] = '\0';
|
|
m_pText = pText + start;
|
|
// If GetLen() is ever cached we would need to update the cache.
|
|
XP_ASSERT(GetLen() == end - start);
|
|
PrintOpen2(ps, FALSE);
|
|
m_pText = pText;
|
|
pText[end] = save;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CEditTextElement::PrintOpen2( CPrintState *ps, XP_Bool bTrimTrailingSpaces )
|
|
{
|
|
#ifdef USE_SCRIPT
|
|
if( m_tf & (TF_SERVER|TF_SCRIPT|TF_STYLE) ){
|
|
PrintLiteral( ps );
|
|
}
|
|
else {
|
|
PrintWithEscapes( ps );
|
|
}
|
|
#else
|
|
PrintWithEscapes( ps, bTrimTrailingSpaces );
|
|
#endif
|
|
}
|
|
|
|
XP_Bool CEditTextElement::Reduce( CEditBuffer *pBuffer ){
|
|
if( GetLen() == 0 ){
|
|
if( pBuffer->m_pCurrent == this
|
|
|| (IsFirstInContainer()
|
|
&& LeafInContainerAfter() == 0)
|
|
){
|
|
return FALSE;
|
|
}
|
|
else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void CEditTextElement::ValidateTree(){
|
|
CEditLeafElement::ValidateTree();
|
|
LO_TextStruct *pText = (LO_TextStruct*) GetLayoutElement();
|
|
|
|
if( GetType() != P_TEXT ){
|
|
XP_ASSERT(FALSE);
|
|
}
|
|
if(m_pText && pText ) {
|
|
if (pText->edit_element != this){
|
|
XP_ASSERT(FALSE);
|
|
}
|
|
if (pText->edit_offset != 0){
|
|
XP_ASSERT(FALSE);
|
|
}
|
|
}
|
|
XP_Bool bHasLinkTag = (m_tf & TF_HREF) != 0;
|
|
XP_Bool bHasLinkRef = (m_href != ED_LINK_ID_NONE);
|
|
if( bHasLinkTag != bHasLinkRef ){
|
|
XP_ASSERT(FALSE);
|
|
};
|
|
}
|
|
#endif // DEBUG
|
|
|
|
void CEditTextElement::StreamToPositionalText( IStreamOut *pOut, XP_Bool bEnd ){
|
|
if( !bEnd ){
|
|
char space = ' ';
|
|
|
|
// Ignore Quoted text, unless there is a selection.
|
|
if ( InMailQuote() && !EDT_IsSelected(GetEditBuffer()->m_pContext)) {
|
|
// Write out spaces instead of text. This prevents us from spell checking
|
|
// quoted text.
|
|
int32 len = GetLen();
|
|
for(int32 i = 0; i < len; i++){
|
|
pOut->Write(&space, 1);
|
|
}
|
|
}
|
|
else {
|
|
INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(GetEditBuffer()->m_pContext);
|
|
int16 csid = INTL_GetCSIWinCSID(c);
|
|
|
|
if (INTL_CharSetType(csid) == SINGLEBYTE){
|
|
pOut->Write(GetText(),GetLen());
|
|
}
|
|
else{
|
|
// Replace any multi-byte characters with spaces, to avoid spell checking
|
|
// of Japanese and other far-eastern text.
|
|
int32 textLen, charLen=0;
|
|
char *pText = GetText();
|
|
|
|
for (textLen = GetLen(); textLen > 0; textLen -= charLen, pText += charLen){
|
|
charLen = INTL_CharLen(csid, (unsigned char *)pText);
|
|
if (charLen > 1){
|
|
for(int32 i = 0; i < charLen; i++){
|
|
pOut->Write(&space, 1);
|
|
}
|
|
}
|
|
else{
|
|
pOut->Write(pText, charLen);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Insertion routines.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//
|
|
// Split text object into two text objects. Parent is the same.
|
|
//
|
|
CEditElement* CEditTextElement::SplitText( int iOffset ){
|
|
CEditTextElement* pNew = new CEditTextElement( 0, m_pText+iOffset );
|
|
m_pText[iOffset] = 0;
|
|
GetParent()->Split( this, pNew, &CEditElement::SplitContainerTest, 0 );
|
|
return pNew;
|
|
}
|
|
|
|
// Copy
|
|
void CEditTextElement::CopyTextFormat( CEditTextElement *pTextElement )
|
|
{
|
|
if( pTextElement )
|
|
{
|
|
pTextElement->m_tf = m_tf;
|
|
pTextElement->m_iFontSize = m_iFontSize;
|
|
pTextElement->m_color = m_color;
|
|
pTextElement->SetHREF(m_href);
|
|
|
|
if ( m_pFace )
|
|
pTextElement->SetFontFace(m_pFace);
|
|
|
|
pTextElement->m_iWeight = m_iWeight;
|
|
pTextElement->m_iPointSize = m_iPointSize;
|
|
if ( m_pScriptExtra )
|
|
pTextElement->SetScriptExtra(m_pScriptExtra);
|
|
}
|
|
}
|
|
|
|
CEditTextElement* CEditTextElement::CopyEmptyText( CEditElement *pParent )
|
|
{
|
|
CEditTextElement* pNew = new CEditTextElement(pParent, 0);
|
|
CopyTextFormat(pNew);
|
|
return pNew;
|
|
}
|
|
|
|
void CEditTextElement::DeleteText(){
|
|
CEditElement *pKill = this;
|
|
CEditElement *pParent;
|
|
|
|
do {
|
|
pParent = pKill->GetParent();
|
|
pKill->Unlink();
|
|
delete pKill;
|
|
pKill = pParent;
|
|
} while( BitSet( edt_setCharFormat, pKill->GetType() ) && pKill->GetChild() == 0 );
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// CEditImageElement
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
// LTNOTE: these should be static functions on CEditImageData..
|
|
//
|
|
EDT_ImageData* edt_NewImageData(){
|
|
EDT_ImageData *pData = XP_NEW( EDT_ImageData );
|
|
if( pData == 0 ){
|
|
// throw();
|
|
return pData;
|
|
}
|
|
pData->bIsMap = FALSE;
|
|
// pData->pUseMap = 0; now in pExtra
|
|
pData->align = ED_ALIGN_DEFAULT;
|
|
pData->pSrc = 0;
|
|
pData->pLowSrc = 0;
|
|
pData->pName = 0;
|
|
pData->pAlt = 0;
|
|
pData->iWidth = 0;
|
|
pData->iHeight = 0;
|
|
pData->iOriginalWidth = 0;
|
|
pData->iOriginalHeight = 0;
|
|
pData->bWidthPercent = FALSE;
|
|
pData->bHeightPercent = FALSE;
|
|
pData->iHSpace = 0;
|
|
pData->iVSpace = 0;
|
|
pData->iBorder = -1;
|
|
pData->bNoSave = FALSE;
|
|
pData->pHREFData = NULL;
|
|
pData->pExtra = 0;
|
|
return pData;
|
|
}
|
|
|
|
EDT_ImageData* edt_DupImageData( EDT_ImageData *pOldData ){
|
|
EDT_ImageData *pData = XP_NEW( EDT_ImageData );
|
|
|
|
// pData->pUseMap = edt_StrDup(pOldData->pUseMap); now in pExtra
|
|
pData->pSrc = edt_StrDup(pOldData->pSrc);
|
|
pData->pName = edt_StrDup(pOldData->pName);
|
|
pData->pLowSrc = edt_StrDup(pOldData->pLowSrc);
|
|
pData->pAlt = edt_StrDup(pOldData->pAlt);
|
|
pData->pExtra = edt_StrDup(pOldData->pExtra);
|
|
|
|
pData->bIsMap = pOldData->bIsMap;
|
|
pData->align = pOldData->align;
|
|
pData->iWidth = pOldData->iWidth;
|
|
pData->iHeight = pOldData->iHeight;
|
|
pData->iOriginalWidth = pOldData->iOriginalWidth;
|
|
pData->iOriginalHeight = pOldData->iOriginalHeight;
|
|
pData->bWidthPercent = pOldData->bWidthPercent;
|
|
pData->bHeightPercent = pOldData->bHeightPercent;
|
|
pData->iHSpace = pOldData->iHSpace;
|
|
pData->iVSpace = pOldData->iVSpace;
|
|
pData->iBorder = pOldData->iBorder;
|
|
pData->bNoSave = pOldData->bNoSave;
|
|
|
|
if( pOldData->pHREFData ){
|
|
pData->pHREFData = EDT_DupHREFData( pOldData->pHREFData );
|
|
}
|
|
else {
|
|
pData->pHREFData = 0;
|
|
}
|
|
|
|
return pData;
|
|
}
|
|
|
|
|
|
void edt_FreeImageData( EDT_ImageData *pData ){
|
|
// if( pData->pUseMap ) XP_FREE( pData->pUseMap ); now in pExtra
|
|
if( pData->pSrc ) XP_FREE( pData->pSrc );
|
|
if( pData->pLowSrc ) XP_FREE( pData->pLowSrc );
|
|
if( pData->pName ) XP_FREE( pData->pName );
|
|
if( pData->pAlt ) XP_FREE( pData->pAlt );
|
|
if( pData->pExtra ) XP_FREE( pData->pExtra );
|
|
if( pData->pHREFData ) EDT_FreeHREFData( pData->pHREFData );
|
|
XP_FREE( pData );
|
|
}
|
|
|
|
//
|
|
// Constructors, Streamers..
|
|
//
|
|
CEditImageElement::CEditImageElement( CEditElement *pParent, PA_Tag* pTag, int16 csid,
|
|
ED_LinkId href ):
|
|
CEditLeafElement( pParent, P_IMAGE ),
|
|
m_pLoImage(0),
|
|
m_pParams(0),
|
|
m_iHeight(0),
|
|
m_iWidth(0),
|
|
m_bWidthPercent(0),
|
|
m_bHeightPercent(0),
|
|
m_iSaveIndex(0),
|
|
m_iSaveLowIndex(0),
|
|
m_href(ED_LINK_ID_NONE),
|
|
m_align( ED_ALIGN_DEFAULT),
|
|
m_bSizeWasGiven(FALSE),
|
|
m_bSizeIsBogus(FALSE)
|
|
{
|
|
// must set href before calling SetImageData to allow GetDefaultBorder to work
|
|
if( href != ED_LINK_ID_NONE ){
|
|
SetHREF( href );
|
|
}
|
|
if( pTag ){
|
|
EDT_ImageData *pData = ParseParams( pTag, csid );
|
|
SetImageData( pData );
|
|
edt_FreeImageData( pData );
|
|
}
|
|
}
|
|
|
|
CEditImageElement::CEditImageElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer):
|
|
CEditLeafElement(pStreamIn, pBuffer),
|
|
m_pLoImage(0),
|
|
m_pParams(0),
|
|
m_href(ED_LINK_ID_NONE),
|
|
m_align(ED_ALIGN_DEFAULT) {
|
|
m_align = (ED_Alignment) pStreamIn->ReadInt();
|
|
m_bSizeWasGiven = pStreamIn->ReadInt() ? TRUE : FALSE;
|
|
m_bSizeIsBogus = pStreamIn->ReadInt() ? TRUE : FALSE;
|
|
m_iHeight = pStreamIn->ReadInt();
|
|
m_iWidth = pStreamIn->ReadInt();
|
|
m_bWidthPercent = pStreamIn->ReadInt();
|
|
m_bHeightPercent = pStreamIn->ReadInt();
|
|
m_pParams = pStreamIn->ReadZString();
|
|
char *pHrefString = pStreamIn->ReadZString();
|
|
char *pExtra = pStreamIn->ReadZString();
|
|
if( pHrefString ){
|
|
SetHREF( pBuffer->linkManager.Add( pHrefString, pExtra ));
|
|
XP_FREE( pHrefString );
|
|
if( pExtra ) XP_FREE( pExtra );
|
|
}
|
|
}
|
|
|
|
CEditImageElement::~CEditImageElement(){
|
|
DisconnectLayoutElements((LO_Element*) m_pLoImage);
|
|
if( m_pParams) XP_FREE( m_pParams );
|
|
if( m_href != ED_LINK_ID_NONE ) m_href->Release();
|
|
}
|
|
|
|
// Return TRUE if pURL has changed.
|
|
PRIVATE XP_Bool edt_make_image_relative(char *pBase,char **pURL) {
|
|
if (!pBase || !*pBase || !pURL || !*pURL || !**pURL) {
|
|
// Base and pURL must be existing non-empty strings.
|
|
return FALSE;
|
|
}
|
|
|
|
if ((*pURL)[0] == '/' || !EDT_IsImageURL(*pURL)) {
|
|
// Don't change URL if using absolute pathing.
|
|
// or not really an image file
|
|
return FALSE;
|
|
}
|
|
|
|
char *pAbs = NET_MakeAbsoluteURL(pBase,*pURL); // In case pURL was already relative.
|
|
char *pRel;
|
|
NET_MakeRelativeURL(pBase,pAbs,&pRel);
|
|
XP_FREEIF(pAbs);
|
|
|
|
if (pRel && XP_STRCMP(pRel,*pURL)) {
|
|
// pRel exists and is different than original value.
|
|
XP_FREEIF(*pURL);
|
|
*pURL = pRel;
|
|
return TRUE;
|
|
}
|
|
else {
|
|
XP_FREEIF(pRel);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
void CEditImageElement::SetLayoutElement( intn iEditOffset, intn lo_type,
|
|
LO_Element* pLoElement ){
|
|
SetLayoutElementHelper(LO_IMAGE, (LO_Element**) &m_pLoImage,
|
|
iEditOffset, lo_type, pLoElement);
|
|
// Zero out the image_req field. We "know" that it is garbage at this point.
|
|
pLoElement->lo_image.image_req = 0;
|
|
|
|
// Make image URLs relative to document. bug 41009.
|
|
EDT_ImageData *pData = GetImageData();
|
|
CEditBuffer *pBuffer = GetEditBuffer();
|
|
if( pData ) {
|
|
XP_Bool bChanged = FALSE;
|
|
if (pBuffer && !EDT_IS_NEW_DOCUMENT(pBuffer->m_pContext)){
|
|
if (edt_make_image_relative(pBuffer->GetBaseURL(),&pData->pSrc)) {
|
|
bChanged = TRUE;
|
|
}
|
|
if (edt_make_image_relative(pBuffer->GetBaseURL(),&pData->pLowSrc)) {
|
|
bChanged = TRUE;
|
|
}
|
|
}
|
|
|
|
if (bChanged) {
|
|
SetImageData(pData);
|
|
}
|
|
edt_FreeImageData(pData);
|
|
}
|
|
}
|
|
|
|
void CEditImageElement::ResetLayoutElement( intn iEditOffset,
|
|
LO_Element* pLoElement ){
|
|
ResetLayoutElementHelper((LO_Element**) &m_pLoImage,
|
|
iEditOffset, pLoElement);
|
|
}
|
|
|
|
LO_Element* CEditImageElement::GetLayoutElement(){
|
|
return (LO_Element*)m_pLoImage;
|
|
}
|
|
|
|
void CEditImageElement::StreamOut( IStreamOut *pOut){
|
|
CEditLeafElement::StreamOut( pOut );
|
|
|
|
pOut->WriteInt( (int32)m_align );
|
|
pOut->WriteInt( m_bSizeWasGiven );
|
|
pOut->WriteInt( m_bSizeIsBogus );
|
|
pOut->WriteInt( m_iHeight );
|
|
pOut->WriteInt( m_iWidth );
|
|
pOut->WriteInt( m_bWidthPercent );
|
|
pOut->WriteInt( m_bHeightPercent );
|
|
|
|
// We need to make the Image Src and LowSrc Parameters absolute.
|
|
// We do this by first converting the parameter string to and Edit
|
|
// data structure and then making the paths absolute.
|
|
EDT_ImageData *pData = GetImageData();
|
|
CEditBuffer *pBuffer = GetEditBuffer();
|
|
if( pData && pBuffer && pBuffer->GetBaseURL() ){
|
|
char *p;
|
|
if( pData->pSrc ) {
|
|
p = NET_MakeAbsoluteURL( pBuffer->GetBaseURL(), pData->pSrc );
|
|
if( p ){
|
|
XP_FREE( pData->pSrc );
|
|
pData->pSrc = p;
|
|
}
|
|
}
|
|
if( pData->pLowSrc ) {
|
|
p = NET_MakeAbsoluteURL( pBuffer->GetBaseURL(), pData->pLowSrc );
|
|
if( p ){
|
|
XP_FREE( pData->pLowSrc );
|
|
pData->pLowSrc = p;
|
|
}
|
|
}
|
|
|
|
// Format the parameters for output.
|
|
p = FormatParams( pData, TRUE );
|
|
pOut->WriteZString( p );
|
|
XP_FREE(p);
|
|
edt_FreeImageData( pData );
|
|
}
|
|
else {
|
|
pOut->WriteZString( m_pParams );
|
|
}
|
|
|
|
|
|
if( m_href != ED_LINK_ID_NONE ){
|
|
// Must make URLs absolute when copying
|
|
CEditBuffer *pBuffer = GetEditBuffer();
|
|
char *pAbsolute = NULL;
|
|
if( pBuffer )
|
|
pAbsolute = NET_MakeAbsoluteURL(LO_GetBaseURL(pBuffer->m_pContext), m_href->hrefStr);
|
|
|
|
if( pAbsolute )
|
|
{
|
|
pOut->WriteZString( pAbsolute );
|
|
XP_FREE(pAbsolute);
|
|
}
|
|
else
|
|
pOut->WriteZString( m_href->hrefStr );
|
|
|
|
pOut->WriteZString( m_href->pExtra );
|
|
}
|
|
else {
|
|
pOut->WriteZString( 0 );
|
|
pOut->WriteZString( 0 );
|
|
}
|
|
}
|
|
|
|
EEditElementType CEditImageElement::GetElementType()
|
|
{
|
|
return eImageElement;
|
|
}
|
|
|
|
PA_Tag* CEditImageElement::TagOpen( int /* iEditOffset */ ){
|
|
char *pCur = 0;
|
|
PA_Tag *pRetTag = 0;
|
|
PA_Tag *pEndTag = 0;
|
|
|
|
if( m_href != ED_LINK_ID_NONE ){
|
|
char *pBuf = PR_smprintf("HREF=%s %s>", edt_MakeParamString(m_href->hrefStr),
|
|
(m_href->pExtra ? m_href->pExtra : "") );
|
|
edt_AddTag( pRetTag, pEndTag, P_ANCHOR, FALSE, pBuf );
|
|
free( pBuf );
|
|
}
|
|
|
|
if( m_pParams ){
|
|
if( SizeIsKnown() ){
|
|
// Append the HEIGHT and WIDTH params, taking into account "%" mode
|
|
pCur = XP_STRDUP(m_pParams);
|
|
|
|
if( m_bHeightPercent ){
|
|
pCur = PR_sprintf_append( pCur, " HEIGHT=\"%ld%%\" ",
|
|
(long)min(100,max(1,m_iHeight)) );
|
|
}
|
|
else {
|
|
pCur = PR_sprintf_append( pCur, " HEIGHT=\"%ld\" ", (long)m_iHeight );
|
|
}
|
|
|
|
if( m_bWidthPercent ){
|
|
pCur = PR_sprintf_append( pCur, " WIDTH=\"%ld%%\" ",
|
|
(long)min(100,max(1,m_iWidth)) );
|
|
}
|
|
else {
|
|
pCur = PR_sprintf_append( pCur, " WIDTH=\"%ld\" ", (long)m_iWidth );
|
|
}
|
|
//CLM: Not sure if this is best behavior, but if size is
|
|
// already set in HTML, should consider that to be "original"?
|
|
// Maybe we should fill in here if we will end up getting same values in FinishedLoad
|
|
//m_iOriginalHeight = m_iHeight;
|
|
//m_iOriginalWidth = m_iWidth;
|
|
}
|
|
else {
|
|
// We don't know the size, but if we don't give a size,
|
|
// and if the image isn't known, we will block and
|
|
// crash. So we put in a bogus size here, and take it
|
|
// out in FinishedLoad if there's image data available.
|
|
m_bSizeIsBogus = TRUE;
|
|
pCur = PR_smprintf("%s HEIGHT=24 WIDTH=22", m_pParams );
|
|
}
|
|
}
|
|
|
|
// For editing, we don't support baseline, left or right alignment of images.
|
|
|
|
if( m_align != ED_ALIGN_DEFAULT && m_align != ED_ALIGN_BASELINE
|
|
&& m_align != ED_ALIGN_LEFT && m_align != ED_ALIGN_RIGHT ){
|
|
pCur = PR_sprintf_append(pCur, "ALIGN=%s ", EDT_AlignString(m_align) );
|
|
}
|
|
|
|
pCur = PR_sprintf_append(pCur, ">");
|
|
|
|
edt_AddTag( pRetTag, pEndTag, P_IMAGE, FALSE, pCur );
|
|
pEndTag->edit_element = this;
|
|
|
|
if( m_href != ED_LINK_ID_NONE ){
|
|
edt_AddTag( pRetTag, pEndTag, P_ANCHOR, TRUE );
|
|
}
|
|
free( pCur );
|
|
return pRetTag;
|
|
}
|
|
|
|
void CEditImageElement::PrintOpen( CPrintState *pPrintState ){
|
|
if( m_href != ED_LINK_ID_NONE ){
|
|
char *pS1 = "";
|
|
char *pS2 = "";
|
|
if( m_href->pExtra ){
|
|
pS1 = " ";
|
|
pS2 = m_href->pExtra;
|
|
}
|
|
//added '\n' to HREF for visual purposes
|
|
pPrintState->m_iCharPos += pPrintState->m_pOut->Printf(
|
|
"<A HREF=%s%s%s>\n", edt_MakeParamString(m_href->hrefStr), pS1, pS2 );
|
|
}
|
|
|
|
// Use special versions of params which has BORDER removed if it's the default
|
|
|
|
char* pParams = NULL;
|
|
{
|
|
EDT_ImageData* pData = GetImageData();
|
|
pParams = FormatParams(pData, TRUE);
|
|
EDT_FreeImageData(pData);
|
|
}
|
|
|
|
if( SizeIsKnown() && ! m_bSizeIsBogus ){
|
|
pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "<IMG %s", (pParams ? pParams : " ") );
|
|
|
|
if( m_bHeightPercent ){
|
|
pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "HEIGHT=%ld%%", (long)m_iHeight );
|
|
} else {
|
|
pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "HEIGHT=%ld", (long)m_iHeight );
|
|
}
|
|
if( m_bWidthPercent ){
|
|
pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( " WIDTH=%ld%%", (long)m_iWidth );
|
|
} else {
|
|
pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( " WIDTH=%ld", (long)m_iWidth );
|
|
}
|
|
}
|
|
else {
|
|
pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "<IMG %s",
|
|
(pParams ? pParams : " " ));
|
|
}
|
|
|
|
if ( pParams )
|
|
XP_FREE(pParams);
|
|
|
|
if( m_align != ED_ALIGN_DEFAULT && m_align != ED_ALIGN_BASELINE ){
|
|
pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( " ALIGN=%s",
|
|
EDT_AlignString(m_align));
|
|
}
|
|
|
|
pPrintState->m_pOut->Write( ">", 1 );
|
|
pPrintState->m_iCharPos++;
|
|
|
|
if( m_href != ED_LINK_ID_NONE ){
|
|
pPrintState->m_iCharPos += pPrintState->m_pOut->Printf(
|
|
"</A>" );
|
|
}
|
|
|
|
}
|
|
|
|
static char *imageParams[] = {
|
|
PARAM_ISMAP,
|
|
// PARAM_USEMAP, Now in extra
|
|
PARAM_SRC,
|
|
PARAM_LOWSRC,
|
|
PARAM_NAME,
|
|
PARAM_ALT,
|
|
PARAM_WIDTH,
|
|
PARAM_HEIGHT,
|
|
PARAM_ALIGN,
|
|
PARAM_HSPACE,
|
|
PARAM_VSPACE,
|
|
PARAM_BORDER,
|
|
PARAM_ALIGN,
|
|
PARAM_NOSAVE,
|
|
0
|
|
};
|
|
|
|
EDT_ImageData* CEditImageElement::ParseParams( PA_Tag *pTag, int16 csid ){
|
|
PA_Block buff;
|
|
EDT_ImageData* pData = edt_NewImageData();
|
|
|
|
buff = PA_FetchParamValue(pTag, PARAM_ISMAP, csid);
|
|
if (buff != NULL){
|
|
pData->bIsMap = TRUE;
|
|
PA_FREE(buff);
|
|
}
|
|
buff = PA_FetchParamValue(pTag, PARAM_NOSAVE, csid);
|
|
if (buff != NULL){
|
|
pData->bNoSave = TRUE;
|
|
PA_FREE(buff);
|
|
}
|
|
pData->pSrc = edt_FetchParamString( pTag, PARAM_SRC, csid );
|
|
pData->pLowSrc = edt_FetchParamString( pTag, PARAM_LOWSRC, csid );
|
|
pData->pName = edt_FetchParamString( pTag, PARAM_NAME, csid );
|
|
pData->pAlt = edt_FetchParamString( pTag, PARAM_ALT, csid );
|
|
|
|
// Fill in the ALT text string with the filename if none is supplied in HTML
|
|
if( pData->pSrc && !pData->pAlt )
|
|
{
|
|
char *pName = EDT_GetFilename(pData->pSrc, FALSE);
|
|
if( pName )
|
|
{
|
|
pData->pAlt = XP_STRDUP(pName);
|
|
XP_FREE(pName);
|
|
}
|
|
}
|
|
|
|
// Get width dimension from string and parse for "%" Default = 100%
|
|
edt_FetchDimension( pTag, PARAM_WIDTH,
|
|
&pData->iWidth, &pData->bWidthPercent,
|
|
0, FALSE, csid );
|
|
edt_FetchDimension( pTag, PARAM_HEIGHT,
|
|
&pData->iHeight, &pData->bHeightPercent,
|
|
0, FALSE, csid );
|
|
m_bSizeWasGiven = pData->iWidth != 0 && pData->iHeight != 0;
|
|
|
|
pData->align = edt_FetchParamAlignment( pTag, ED_ALIGN_BASELINE, FALSE, csid );
|
|
|
|
pData->iHSpace = edt_FetchParamInt( pTag, PARAM_HSPACE, 0, csid );
|
|
pData->iVSpace = edt_FetchParamInt( pTag, PARAM_VSPACE, 0, csid );
|
|
pData->iBorder = edt_FetchParamInt( pTag, PARAM_BORDER, -1, csid );
|
|
pData->pHREFData = 0;
|
|
|
|
pData->pExtra = edt_FetchParamExtras( pTag, imageParams, csid );
|
|
|
|
return pData;
|
|
}
|
|
|
|
EDT_ImageData* CEditImageElement::GetImageData(){
|
|
EDT_ImageData *pRet;
|
|
|
|
// LTNOTE: hackola time. We don't want TagOpen to return multiple tags
|
|
// in this use, so we prevent it by faking out the href ID and restoring
|
|
// it when we are done. Not real maintainable.
|
|
|
|
ED_LinkId saveHref = m_href;
|
|
m_href = ED_LINK_ID_NONE;
|
|
PA_Tag* pTag = TagOpen(0);
|
|
m_href = saveHref;
|
|
|
|
pRet = ParseParams( pTag, GetWinCSID() );
|
|
|
|
if ( m_bSizeIsBogus ) {
|
|
pRet->iWidth = 0; pRet->bWidthPercent = FALSE;
|
|
pRet->iHeight = 0; pRet->bHeightPercent = FALSE;
|
|
}
|
|
|
|
//CLM: We stored this when image was first loaded:
|
|
int originalWidth = 0;
|
|
int originalHeight = 0;
|
|
if ( m_pLoImage ) {
|
|
IL_GetNaturalDimensions( m_pLoImage->image_req, &originalWidth, &originalHeight);
|
|
}
|
|
pRet->iOriginalWidth = originalWidth;
|
|
pRet->iOriginalHeight = originalHeight;
|
|
|
|
pRet->bWidthPercent = m_bWidthPercent;
|
|
pRet->bHeightPercent = m_bHeightPercent;
|
|
|
|
if( m_href != ED_LINK_ID_NONE )
|
|
pRet->pHREFData = m_href->GetData();
|
|
|
|
// We need to set the link border if there's an HREF or USEMAP
|
|
// Unfortunately, we don't explicitly handle USEMAP any more
|
|
// so we must fish for it in the tag. We don't worry about win_csid
|
|
// (last param) since we just want to know if string exists
|
|
char *pUsemap = edt_FetchParamString(pTag, PARAM_USEMAP, CS_FE_ASCII );
|
|
|
|
if( pUsemap ||
|
|
m_href != ED_LINK_ID_NONE )
|
|
{
|
|
// The only way to consistently show the "default" 2-pixel border
|
|
// is to set it to 2 if it was missing in the tag params
|
|
if( pRet->iBorder == -1 )
|
|
pRet->iBorder = 2; //Don't use GetDefaultBorder - doesn't consider USEMAP
|
|
}
|
|
pRet->align = m_align;
|
|
PA_FreeTag( pTag );
|
|
XP_FREEIF(pUsemap);
|
|
return pRet;
|
|
}
|
|
|
|
// border is weird. If we are in a link the border is default 2, if
|
|
// we are not in a link, border is default 0. In order not to foist this
|
|
// weirdness on the user, we are always explicit on our image borders.
|
|
// This isn't correct - it doesn't consider if there's a USEMAP
|
|
// 9/29/98: This was used by FEs, but now that we always set a border value
|
|
// instead of trying to maintain the -1 that means no BORDER param is written,
|
|
// this isn't called, so let's not worry about USEMAP
|
|
int32 CEditImageElement::GetDefaultBorder(){
|
|
return ( m_href != ED_LINK_ID_NONE ) ? 2 : 0;
|
|
}
|
|
|
|
XP_Bool CEditImageElement::GetLOElementAndOffset( ElementOffset iEditOffset, XP_Bool /*bEditStickyAfter*/ ,
|
|
LO_Element*& pRetElement,
|
|
int& pLayoutOffset ){
|
|
pLayoutOffset = 0;
|
|
pRetElement = (LO_Element*)m_pLoImage;
|
|
pLayoutOffset = (int) iEditOffset;
|
|
return TRUE;
|
|
}
|
|
|
|
void CEditImageElement::SetImageData( EDT_ImageData *pData ){
|
|
char* pCur = FormatParams(pData, FALSE);
|
|
|
|
if( m_pParams ){
|
|
XP_FREE( m_pParams );
|
|
}
|
|
m_pParams = pCur;
|
|
|
|
m_align = pData->align;
|
|
|
|
if (pData->iHeight != 0 && pData->iWidth != 0 ) {
|
|
m_bSizeWasGiven = TRUE;
|
|
m_bSizeIsBogus = FALSE;
|
|
m_bHeightPercent = pData->bHeightPercent;
|
|
m_bWidthPercent = pData->bWidthPercent;
|
|
}
|
|
else {
|
|
m_bSizeWasGiven = FALSE;
|
|
//CLM: We were given Width or Height = 0
|
|
// so we will get REAL dimensions
|
|
// Clearing these willl allow us to retrieve actual dimensions
|
|
// on an image that had WIDTH and/or HEIGHT values on 1st load
|
|
m_bHeightPercent = 0;
|
|
m_bWidthPercent = 0;
|
|
}
|
|
m_iHeight = pData->iHeight;
|
|
m_iWidth = pData->iWidth;
|
|
}
|
|
|
|
char* CEditImageElement::FormatParams(EDT_ImageData* pData, XP_Bool bForPrinting){
|
|
char *pCur = 0;
|
|
|
|
if( pData->bIsMap ){
|
|
pCur = PR_sprintf_append( pCur, "ISMAP " );
|
|
}
|
|
|
|
/* USEMAP now in pExtra
|
|
if( pData->pUseMap ){
|
|
pCur = PR_sprintf_append( pCur, "USEMAP=%s ", edt_MakeParamString(pData->pUseMap) );
|
|
}
|
|
*/
|
|
|
|
if( pData->pSrc ){
|
|
pCur = PR_sprintf_append( pCur, "SRC=%s ", edt_MakeParamString( pData->pSrc ) );
|
|
}
|
|
|
|
if( pData->pLowSrc ){
|
|
pCur = PR_sprintf_append( pCur, "LOWSRC=%s ", edt_MakeParamString( pData->pLowSrc ) );
|
|
}
|
|
|
|
if( pData->pName ){
|
|
pCur = PR_sprintf_append( pCur, "NAME=%s ", edt_MakeParamString( pData->pName ) );
|
|
}
|
|
|
|
if( pData->pAlt ){
|
|
pCur = PR_sprintf_append( pCur, "ALT=%s ", edt_MakeParamString( pData->pAlt ) );
|
|
}
|
|
|
|
if( pData->iHSpace ){
|
|
pCur = PR_sprintf_append( pCur, "HSPACE=%ld ", (long)pData->iHSpace );
|
|
}
|
|
|
|
if( pData->iVSpace ){
|
|
pCur = PR_sprintf_append( pCur, "VSPACE=%ld ", (long)pData->iVSpace );
|
|
}
|
|
|
|
if( pData->bNoSave ){
|
|
pCur = PR_sprintf_append( pCur, "NOSAVE " );
|
|
}
|
|
|
|
// border is weird. If we are in a link the border is default 2, if
|
|
// we are not in a link, border is default 0. When printing, we only write out a
|
|
// border if it's different than the default.
|
|
//
|
|
// Other times, we always write it out because we want it to stay the same even if the
|
|
// default border size changes. Whew!
|
|
{
|
|
if( !bForPrinting || ( pData->iBorder >=0 )){
|
|
pCur = PR_sprintf_append( pCur, "BORDER=%ld ", (long)pData->iBorder );
|
|
}
|
|
}
|
|
|
|
if( pData->pExtra != 0 ){
|
|
pCur = PR_sprintf_append( pCur, "%s ", pData->pExtra);
|
|
}
|
|
return pCur;
|
|
}
|
|
|
|
void CEditImageElement::FinishedLoad( CEditBuffer *pBuffer ){
|
|
if( m_pLoImage ){
|
|
if ( ! m_bSizeWasGiven || m_bSizeIsBogus ) {
|
|
// The user didn't specify a size. Look at what we got.
|
|
m_iHeight = pBuffer->m_pContext->convertPixY * m_pLoImage->height;
|
|
m_iWidth = pBuffer->m_pContext->convertPixX * m_pLoImage->width;
|
|
// This fixes lots of image size problems (like bug 148158),
|
|
// but we will ALWAYS write out the size, even if not in original HTML
|
|
m_bSizeIsBogus = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CEditImageElement::SetHREF( ED_LinkId id ){
|
|
if( m_href != ED_LINK_ID_NONE ){
|
|
m_href->Release();
|
|
}
|
|
m_href = id;
|
|
if( id != ED_LINK_ID_NONE ){
|
|
id->AddRef();
|
|
}
|
|
}
|
|
|
|
XP_Bool CEditImageElement::SizeIsKnown() {
|
|
return m_iHeight > 0;
|
|
}
|
|
|
|
void CEditImageElement::MaskData( EDT_CharacterData*& pData ){
|
|
if( pData == 0 ){
|
|
pData = GetCharacterData();
|
|
return;
|
|
}
|
|
|
|
// An image can have an HREF text format
|
|
ED_TextFormat tf = ( m_href == ED_LINK_ID_NONE ) ? 0 : TF_HREF;
|
|
|
|
ED_TextFormat differences = pData->values ^ tf;
|
|
if( differences ){
|
|
pData->mask &= ~differences;
|
|
}
|
|
if( (pData->mask & pData->values & tf & TF_HREF)
|
|
&& (pData->linkId != m_href ) ){
|
|
pData->mask &= ~TF_HREF;
|
|
}
|
|
}
|
|
|
|
EDT_CharacterData* CEditImageElement::GetCharacterData(){
|
|
EDT_CharacterData *pData = EDT_NewCharacterData();
|
|
// We are only interested in HREF?
|
|
// pData->mask = TF_HREF;
|
|
pData->mask = -1;
|
|
if( m_href != ED_LINK_ID_NONE ){
|
|
pData->pHREFData = m_href->GetData();
|
|
pData->values = TF_HREF;
|
|
}
|
|
pData->linkId = m_href;
|
|
return pData;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
// CEditHorizRuleElement
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
CEditHorizRuleElement::CEditHorizRuleElement( CEditElement *pParent, PA_Tag* pTag, int16 /*csid*/ ):
|
|
CEditLeafElement( pParent, P_HRULE ),
|
|
m_pLoHorizRule(0)
|
|
{
|
|
|
|
if( pTag ){
|
|
char *locked_buff;
|
|
|
|
PA_LOCK(locked_buff, char *, pTag->data );
|
|
if( locked_buff ){
|
|
SetTagData( locked_buff );
|
|
}
|
|
PA_UNLOCK(pTag->data);
|
|
}
|
|
}
|
|
|
|
CEditHorizRuleElement::CEditHorizRuleElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer):
|
|
CEditLeafElement(pStreamIn, pBuffer),
|
|
m_pLoHorizRule(0)
|
|
{
|
|
}
|
|
|
|
CEditHorizRuleElement::~CEditHorizRuleElement(){
|
|
DisconnectLayoutElements((LO_Element*) m_pLoHorizRule);
|
|
}
|
|
|
|
|
|
LO_Element* CEditHorizRuleElement::GetLayoutElement(){
|
|
return (LO_Element*)m_pLoHorizRule;
|
|
}
|
|
|
|
void CEditHorizRuleElement::SetLayoutElement( intn iEditOffset, intn lo_type,
|
|
LO_Element* pLoElement ){
|
|
SetLayoutElementHelper(LO_HRULE, (LO_Element**) &m_pLoHorizRule,
|
|
iEditOffset, lo_type, pLoElement);
|
|
}
|
|
|
|
void CEditHorizRuleElement::ResetLayoutElement( intn iEditOffset,
|
|
LO_Element* pLoElement ){
|
|
ResetLayoutElementHelper((LO_Element**) &m_pLoHorizRule,
|
|
iEditOffset, pLoElement);
|
|
}
|
|
|
|
void CEditHorizRuleElement::StreamOut( IStreamOut *pOut ){
|
|
CEditLeafElement::StreamOut( pOut );
|
|
}
|
|
|
|
EEditElementType CEditHorizRuleElement::GetElementType()
|
|
{
|
|
return eHorizRuleElement;
|
|
}
|
|
|
|
XP_Bool CEditHorizRuleElement::GetLOElementAndOffset( ElementOffset iEditOffset, XP_Bool /* bEditStickyAfter */,
|
|
LO_Element*& pRetElement,
|
|
int& pLayoutOffset ){
|
|
pRetElement = (LO_Element*)m_pLoHorizRule;
|
|
pLayoutOffset = (int) iEditOffset;
|
|
return TRUE;
|
|
}
|
|
|
|
// Property Getting and Setting Stuff.
|
|
|
|
EDT_HorizRuleData* CEditHorizRuleElement::NewData(){
|
|
EDT_HorizRuleData *pData = XP_NEW( EDT_HorizRuleData );
|
|
if( pData == 0 ){
|
|
// throw();
|
|
return pData;
|
|
}
|
|
pData->align = ED_ALIGN_CENTER;
|
|
pData->size = DEFAULT_HR_THICKNESS;
|
|
pData->bNoShade = 0;
|
|
pData->iWidth = 100;
|
|
pData->bWidthPercent = TRUE;
|
|
pData->pExtra = 0;
|
|
return pData;
|
|
}
|
|
|
|
void CEditHorizRuleElement::FreeData( EDT_HorizRuleData *pData ){
|
|
if( pData->pExtra ) XP_FREE( pData->pExtra );
|
|
XP_FREE( pData );
|
|
}
|
|
|
|
void CEditHorizRuleElement::SetData( EDT_HorizRuleData *pData ){
|
|
char *pNew = 0;
|
|
|
|
if( pData->align == ED_ALIGN_RIGHT || pData->align == ED_ALIGN_LEFT ){
|
|
pNew = PR_sprintf_append( pNew, "ALIGN=%s ", EDT_AlignString(pData->align) );
|
|
}
|
|
if( pData->size != DEFAULT_HR_THICKNESS ){
|
|
pNew = PR_sprintf_append( pNew, "SIZE=%ld ", (long)pData->size );
|
|
}
|
|
if( pData->bNoShade ){
|
|
pNew = PR_sprintf_append( pNew, "NOSHADE " );
|
|
}
|
|
if( pData->iWidth ){
|
|
if( pData->bWidthPercent ){
|
|
pNew = PR_sprintf_append( pNew, "WIDTH=\"%ld%%\" ",
|
|
(long)min(100,max(1,pData->iWidth)) );
|
|
}
|
|
else {
|
|
pNew = PR_sprintf_append( pNew, "WIDTH=\"%ld\" ", (long)pData->iWidth );
|
|
}
|
|
}
|
|
if( pData->pExtra){
|
|
pNew = PR_sprintf_append( pNew, "%s ", pData->pExtra );
|
|
}
|
|
|
|
pNew = PR_sprintf_append( pNew, ">" );
|
|
|
|
SetTagData( pNew );
|
|
free(pNew);
|
|
}
|
|
|
|
EDT_HorizRuleData* CEditHorizRuleElement::GetData( ){
|
|
EDT_HorizRuleData *pRet;
|
|
PA_Tag* pTag = TagOpen(0);
|
|
pRet = ParseParams( pTag, GetWinCSID() );
|
|
PA_FreeTag( pTag );
|
|
return pRet;
|
|
}
|
|
|
|
static char *hruleParams[] = {
|
|
PARAM_ALIGN,
|
|
PARAM_NOSHADE,
|
|
PARAM_WIDTH,
|
|
PARAM_SIZE,
|
|
0
|
|
};
|
|
|
|
EDT_HorizRuleData* CEditHorizRuleElement::ParseParams( PA_Tag *pTag, int16 csid ){
|
|
EDT_HorizRuleData *pData = NewData();
|
|
ED_Alignment align;
|
|
|
|
align = edt_FetchParamAlignment( pTag, ED_ALIGN_CENTER, FALSE, csid );
|
|
if( align == ED_ALIGN_RIGHT || align == ED_ALIGN_LEFT ){
|
|
pData->align = align;
|
|
}
|
|
// Get width dimension from string and parse for "%" Default = 100%
|
|
edt_FetchDimension( pTag, PARAM_WIDTH,
|
|
&pData->iWidth, &pData->bWidthPercent,
|
|
100, TRUE, csid );
|
|
pData->bNoShade = edt_FetchParamBoolExist( pTag, PARAM_NOSHADE, csid );
|
|
pData->size = edt_FetchParamInt( pTag, PARAM_SIZE, DEFAULT_HR_THICKNESS, csid );
|
|
pData->pExtra = edt_FetchParamExtras( pTag, hruleParams, csid );
|
|
return pData;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CEditIconElement
|
|
//-----------------------------------------------------------------------------
|
|
static char *ppIconTags[] = {
|
|
"align=absbotom border=0 src=\"internal-edit-named-anchor\" alt=\"%s\">",
|
|
"align=absbotom border=0 src=\"internal-edit-form-element\">",
|
|
"src=\"internal-edit-unsupported-tag\" alt=\"<%s\"",
|
|
"src=\"internal-edit-unsupported-end-tag\" alt=\"</%s\"",
|
|
"align=absbotom border=0 src=\"internal-edit-java\">",
|
|
"align=absbotom border=0 src=\"internal-edit-plugin\">",
|
|
0
|
|
};
|
|
|
|
CEditIconElement::CEditIconElement( CEditElement *pParent, int32 iconTag,
|
|
PA_Tag* pTag, int16 /*csid*/ )
|
|
:
|
|
CEditLeafElement( pParent, P_IMAGE ),
|
|
m_originalTagType( pTag ? pTag->type : P_UNKNOWN ),
|
|
m_iconTag( iconTag ),
|
|
m_bEndTag( pTag ? pTag->is_end : 0 ),
|
|
m_pSpoofData(0),
|
|
m_pLoIcon(0),
|
|
m_piSaveIndices(NULL)
|
|
{
|
|
|
|
if( pTag ){
|
|
char *locked_buff;
|
|
|
|
PA_LOCK(locked_buff, char *, pTag->data );
|
|
if( locked_buff ){
|
|
char *p = locked_buff;
|
|
if( m_originalTagType != P_ANCHOR
|
|
&& m_originalTagType != P_UNKNOWN ){
|
|
|
|
while( *p == ' ' ) p++;
|
|
char *pSpace = " ";
|
|
if( *p == '>' ) pSpace = "";
|
|
|
|
char *pTagData = PR_smprintf("%s%s%s",
|
|
EDT_TagString(m_originalTagType), pSpace, p );
|
|
SetTagData( pTagData );
|
|
SetTagData( pTag, pTagData );
|
|
m_originalTagType = P_UNKNOWN;
|
|
free( pTagData );
|
|
}
|
|
else {
|
|
SetTagData( locked_buff );
|
|
}
|
|
}
|
|
PA_UNLOCK(pTag->data);
|
|
SetSpoofData( pTag );
|
|
}
|
|
}
|
|
|
|
CEditIconElement::~CEditIconElement(){
|
|
DisconnectLayoutElements((LO_Element*) m_pLoIcon);
|
|
if( m_pSpoofData ){
|
|
free( m_pSpoofData );
|
|
}
|
|
XP_FREEIF(m_piSaveIndices);
|
|
}
|
|
|
|
CEditIconElement::CEditIconElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer):
|
|
CEditLeafElement(pStreamIn, pBuffer),
|
|
m_pLoIcon(0),
|
|
m_piSaveIndices(NULL)
|
|
{
|
|
m_originalTagType = (TagType) pStreamIn->ReadInt();
|
|
m_iconTag = pStreamIn->ReadInt();
|
|
m_bEndTag = pStreamIn->ReadInt() ? TRUE : FALSE;
|
|
m_pSpoofData = pStreamIn->ReadZString();
|
|
}
|
|
|
|
void CEditIconElement::SetLayoutElement( intn iEditOffset, intn lo_type,
|
|
LO_Element* pLoElement ){
|
|
SetLayoutElementHelper(LO_IMAGE, (LO_Element**) &m_pLoIcon,
|
|
iEditOffset, lo_type, pLoElement);
|
|
}
|
|
|
|
void CEditIconElement::ResetLayoutElement( intn iEditOffset,
|
|
LO_Element* pLoElement ){
|
|
ResetLayoutElementHelper((LO_Element**) &m_pLoIcon,
|
|
iEditOffset, pLoElement);
|
|
}
|
|
|
|
void CEditIconElement::StreamOut( IStreamOut *pOut ){
|
|
CEditLeafElement::StreamOut( pOut );
|
|
pOut->WriteInt( m_originalTagType );
|
|
pOut->WriteInt( m_iconTag );
|
|
pOut->WriteInt( m_bEndTag );
|
|
pOut->WriteZString( m_pSpoofData );
|
|
}
|
|
|
|
XP_Bool CEditIconElement::GetLOElementAndOffset( ElementOffset iEditOffset, XP_Bool /* bEditStickyAfter */,
|
|
LO_Element*& pRetElement,
|
|
int& pLayoutOffset ){
|
|
pLayoutOffset = 0;
|
|
pRetElement = (LO_Element*)m_pLoIcon;
|
|
pLayoutOffset = iEditOffset;
|
|
return TRUE;
|
|
}
|
|
|
|
PA_Tag* CEditIconElement::TagOpen( int /* iEditOffset */ ){
|
|
PA_Tag *pTag = XP_NEW( PA_Tag );
|
|
XP_BZERO( pTag, sizeof( PA_Tag ) );
|
|
SetTagData( pTag, m_pSpoofData );
|
|
return pTag;
|
|
}
|
|
|
|
#define IS_WHITE(p) ((p) == ' ' || (p) == '\r' || (p) == '\n')
|
|
|
|
PRIVATE XP_Bool IsScriptText2(char* pData, char* pName){
|
|
char *p = pData;
|
|
|
|
while( *p && IS_WHITE(*p) ){
|
|
p++;
|
|
}
|
|
if ( !*p || *p != '<' ) {
|
|
return FALSE;
|
|
}
|
|
|
|
p++;
|
|
char *n = pName;
|
|
|
|
while ( *p && * n && ! IS_WHITE(*p) && XP_TO_LOWER(*p) == XP_TO_LOWER(*n) ) {
|
|
p++;
|
|
n++;
|
|
}
|
|
if ( *n != 0 || (*p != '>' && ! IS_WHITE(*p)) ) {
|
|
// We didn't match the tag!
|
|
return FALSE;
|
|
}
|
|
// Go to the end of the string.
|
|
while ( *p ) {
|
|
p++;
|
|
}
|
|
p--;
|
|
// Back up to the last '>'. Nothing but whitespace to last '>'
|
|
while ( p > pData && (*p != '>' && IS_WHITE(*p) ) ) {
|
|
--p;
|
|
}
|
|
if ( p <= pData || *p != '>' ) {
|
|
return FALSE;
|
|
}
|
|
// Back-match to last '<'
|
|
while ( *p && *p != '<' && p > pData ) {
|
|
p--;
|
|
}
|
|
if ( p <= pData || *p != '<' ) {
|
|
return FALSE;
|
|
}
|
|
if ( *++p != '/' ) {
|
|
return FALSE;
|
|
}
|
|
if ( strcasestr(++p, pName) == NULL ) {
|
|
return FALSE;
|
|
}
|
|
p += XP_STRLEN(pName);
|
|
if ( *p != '>' && ! IS_WHITE(*p) ) {
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
PRIVATE XP_Bool IsScriptText(char* pData) {
|
|
// Return TRUE if pData is <SCRIPT>...</SCRIPT>, or <STYLE>...</STYLE>, or <SERVER>...</SERVER>
|
|
return IsScriptText2(pData, "SCRIPT") || IsScriptText2(pData, "STYLE") || IsScriptText2(pData, "SERVER");
|
|
}
|
|
//
|
|
// Do some heuristic validation to make sure
|
|
//
|
|
ED_TagValidateResult CEditIconElement::ValidateTag( char *pData, XP_Bool bNoBrackets ){
|
|
if ( IsScriptText(pData) ) {
|
|
return ED_TAG_OK;
|
|
}
|
|
char *p = pData;
|
|
|
|
while( *p && IS_WHITE(*p) ){
|
|
p++;
|
|
}
|
|
if( !bNoBrackets ){
|
|
if( *p != '<' ){
|
|
return ED_TAG_UNOPENED;
|
|
}
|
|
else {
|
|
p++;
|
|
}
|
|
if( *p == '/' ){
|
|
p++;
|
|
}
|
|
|
|
if( IS_WHITE(*p) ){
|
|
return ED_TAG_TAGNAME_EXPECTED;
|
|
}
|
|
}
|
|
|
|
// Is this a comment?
|
|
XP_Bool isComment = p[0] == '!' && p[1] == '-' && p[2] == '-';
|
|
|
|
if ( isComment ) {
|
|
char* start = p;
|
|
while( *p ){
|
|
p++;
|
|
}
|
|
if ( ! bNoBrackets ) {
|
|
--p;
|
|
}
|
|
|
|
while ( IS_WHITE(*p) && p > start ) {
|
|
p--;
|
|
}
|
|
|
|
XP_Bool endComment = (p >= start + 6) && p[-2] == '-' && p[-1] == '-';
|
|
if ( ! endComment ) {
|
|
return ED_TAG_PREMATURE_CLOSE; // ToDo: Use a specific error message
|
|
}
|
|
}
|
|
else {
|
|
// look for unterminated strings.
|
|
while( *p && *p != '>' ){
|
|
if( *p == '"' || *p == '\'' ){
|
|
char quote = *p++;
|
|
while( *p && *p != quote ){
|
|
p++;
|
|
}
|
|
if( *p == 0 ){
|
|
return ED_TAG_UNTERMINATED_STRING;
|
|
}
|
|
}
|
|
p++;
|
|
}
|
|
}
|
|
if( bNoBrackets ){
|
|
if( *p == 0 ){
|
|
return ED_TAG_OK;
|
|
}
|
|
else {
|
|
XP_ASSERT( *p == '>' );
|
|
return ED_TAG_PREMATURE_CLOSE;
|
|
}
|
|
}
|
|
else if( *p == '>' ){
|
|
p++;
|
|
while( IS_WHITE( *p ) ){
|
|
p++;
|
|
}
|
|
if( *p != 0 ){
|
|
return ED_TAG_PREMATURE_CLOSE;
|
|
}
|
|
else {
|
|
return ED_TAG_OK;
|
|
}
|
|
}
|
|
else {
|
|
XP_ASSERT( *p == 0 );
|
|
return ED_TAG_UNCLOSED;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Restore the original tag type so we save the right tag type when we output
|
|
// we print.
|
|
//
|
|
void CEditIconElement::PrintOpen( CPrintState *pPrintState ){
|
|
if( m_originalTagType != P_UNKNOWN ){
|
|
TagType tSave = GetType();
|
|
SetType( m_originalTagType );
|
|
CEditLeafElement::PrintOpen( pPrintState );
|
|
SetType( tSave );
|
|
}
|
|
else {
|
|
if( m_bEndTag ){
|
|
pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "</%s",
|
|
GetTagData());
|
|
}
|
|
else {
|
|
pPrintState->m_iCharPos += pPrintState->m_pOut->Printf( "<%s",
|
|
GetTagData());
|
|
}
|
|
}
|
|
}
|
|
|
|
void CEditIconElement::PrintEnd( CPrintState *pPrintState ){
|
|
if( m_originalTagType != P_UNKNOWN ){
|
|
TagType tSave = GetType();
|
|
SetType( m_originalTagType );
|
|
CEditLeafElement::PrintEnd( pPrintState );
|
|
SetType( tSave );
|
|
}
|
|
}
|
|
|
|
char* CEditIconElement::GetData(){
|
|
int iLen = XP_STRLEN( GetTagData() );
|
|
char *p = (char*)XP_ALLOC( iLen+3 );
|
|
char *pRet = p;
|
|
*p++ = '<';
|
|
if( m_bEndTag ){
|
|
*p++ = '/';
|
|
}
|
|
XP_STRCPY( p, GetTagData() );
|
|
return pRet;
|
|
}
|
|
|
|
|
|
PRIVATE
|
|
void edt_addToStringLists(char **list1,char *start1,char *end1,
|
|
char **list2,char *start2,char *end2,
|
|
int index) {
|
|
int len1 = end1 - start1;
|
|
int len2 = end2 - start2;
|
|
|
|
// Don't allow empty strings.
|
|
if ((len1 > 0) && (len2 > 0)) {
|
|
// space for trailing NULL.
|
|
list1[index] = (char *)XP_ALLOC(len1 + 1);
|
|
list2[index] = (char *)XP_ALLOC(len2 + 1);
|
|
if (!list1[index] || !list2[index]) {
|
|
XP_ASSERT(0);
|
|
return;
|
|
}
|
|
// copy strings.
|
|
XP_MEMCPY(list1[index],start1,len1);
|
|
list1[index][len1] = '\0';
|
|
XP_MEMCPY(list2[index],start2,len2);
|
|
list2[index][len2] = '\0';
|
|
}
|
|
}
|
|
|
|
int CEditIconElement::ParseLocalData(char ***mimeTypes,char ***URLs) {
|
|
int retVal = 0;
|
|
*mimeTypes = NULL;
|
|
*URLs = NULL;
|
|
|
|
// Don't want the version with the spoofData, so call ancestor.
|
|
PA_Tag *pTag = CEditElement::TagOpen(0);
|
|
|
|
if (pTag) {
|
|
// Don't use edt_FetchParamString because it strips out interior
|
|
// white space.
|
|
PA_Block buff = PA_FetchParamValue(pTag,PARAM_LOCALDATA,GetWinCSID());
|
|
if (buff) {
|
|
char *pLocalData;
|
|
PA_LOCK(pLocalData,char *,buff);
|
|
|
|
// maxFiles = 1+number of '+' characters in pLocalData,
|
|
// gives upper bound on size of returned lists.
|
|
int maxFiles = 1;
|
|
char *p = pLocalData;
|
|
while (*p && (p = XP_STRCHR(p,'+')) != NULL) {
|
|
maxFiles++;
|
|
p++; // move past found '+'
|
|
}
|
|
|
|
// Create lists.
|
|
*mimeTypes = (char **)XP_ALLOC(maxFiles * sizeof(char *));
|
|
if (!*mimeTypes) {
|
|
return 0;
|
|
}
|
|
*URLs = (char **)XP_ALLOC(maxFiles * sizeof(char *));
|
|
if (!*URLs) {
|
|
XP_FREEIF(*mimeTypes); // Will zero *mimeTypes.
|
|
return 0;
|
|
}
|
|
|
|
|
|
// reset p.
|
|
p = pLocalData;
|
|
|
|
char *mimeStart=NULL, *mimeEnd=NULL, *URLStart=NULL, *URLEnd=NULL;
|
|
|
|
// State machine to read in string
|
|
int state = BeforeMIME;
|
|
while(*p) {
|
|
switch (state) {
|
|
case BeforeMIME:
|
|
if (!XP_IS_SPACE(*p)) {
|
|
state = InMIME;
|
|
mimeStart = p;
|
|
}
|
|
break;
|
|
case InMIME:
|
|
if (XP_IS_SPACE(*p)) {
|
|
mimeEnd = p;
|
|
state = BeforeURL;
|
|
}
|
|
break;
|
|
case BeforeURL:
|
|
if (!XP_IS_SPACE(*p)) {
|
|
URLStart = p;
|
|
state = InURL;
|
|
}
|
|
break;
|
|
case InURL:
|
|
if (XP_IS_SPACE(*p)) {
|
|
URLEnd = p;
|
|
XP_ASSERT(retVal < maxFiles);
|
|
edt_addToStringLists(*mimeTypes,mimeStart,mimeEnd,
|
|
*URLs,URLStart,URLEnd,
|
|
retVal);
|
|
retVal++;
|
|
state = AfterURL;
|
|
}
|
|
if (*p == '+') {
|
|
URLEnd = p;
|
|
XP_ASSERT(retVal < maxFiles);
|
|
edt_addToStringLists(*mimeTypes,mimeStart,mimeEnd,
|
|
*URLs,URLStart,URLEnd,
|
|
retVal);
|
|
retVal++;
|
|
state = BeforeMIME;
|
|
}
|
|
break;
|
|
case AfterURL:
|
|
if (*p == '+') {
|
|
state = BeforeMIME;
|
|
}
|
|
break;
|
|
default:
|
|
XP_ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
// Move forward.
|
|
p++;
|
|
}
|
|
|
|
// Close out final mime/URL pair.
|
|
if (state == InURL) {
|
|
URLEnd = p;
|
|
XP_ASSERT(retVal < maxFiles);
|
|
edt_addToStringLists(*mimeTypes,mimeStart,mimeEnd,
|
|
*URLs,URLStart,URLEnd,
|
|
retVal);
|
|
retVal++;
|
|
}
|
|
|
|
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
PA_FreeTag(pTag);
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
void CEditIconElement::FreeLocalDataLists(char **mimeTypes,char **URLs,int count) {
|
|
for (int n = 0; n < count; n++) {
|
|
XP_FREEIF(mimeTypes[n]);
|
|
XP_FREEIF(URLs[n]);
|
|
}
|
|
XP_FREEIF(mimeTypes);
|
|
XP_FREEIF(URLs);
|
|
}
|
|
|
|
void CEditIconElement::ReplaceParamValues(char *pOld,char *pNew) {
|
|
// Not changing anything.
|
|
if (!XP_STRCMP(pOld,pNew)) return;
|
|
|
|
// Illegal.
|
|
if (!pOld || !pNew || pOld[0] == '=' || pOld[0] == '\"') return;
|
|
|
|
char *pTagData = GetTagData();
|
|
if (!pTagData) return;
|
|
|
|
int state = OutsideValue;
|
|
char *last = pTagData; // last part copied to pNewTagData.
|
|
char *scan = pTagData; // Scans ahead of last.
|
|
char *pNewTagData = NULL;
|
|
int pOldLen = XP_STRLEN(pOld);
|
|
|
|
StrAllocCat(pNewTagData,"<");
|
|
|
|
while (*scan) {
|
|
XP_ASSERT(scan);
|
|
|
|
switch (state) {
|
|
case OutsideValue:
|
|
if (*scan == '=') {
|
|
state = BeforeValue;
|
|
}
|
|
break;
|
|
case InsideValue:
|
|
if (XP_IS_SPACE(*scan)) {
|
|
// end of non-quoted value.
|
|
state = OutsideValue;
|
|
}
|
|
break;
|
|
case InsideValueQuote:
|
|
if (*scan == '\"') {
|
|
// closing quote.
|
|
state = OutsideValue;
|
|
}
|
|
break;
|
|
case BeforeValue:
|
|
if (*scan == '\"') {
|
|
state = InsideValueQuote;
|
|
}
|
|
else if (!XP_IS_SPACE(*scan)) {
|
|
state = InsideValue;
|
|
}
|
|
// else still BeforeValue
|
|
break;
|
|
default:
|
|
XP_ASSERT(0);
|
|
}
|
|
|
|
if (state == InsideValue || state == InsideValueQuote) {
|
|
// Found an instance.
|
|
if (!XP_STRNCMP(scan,pOld,pOldLen)) {
|
|
// flush last part.
|
|
if (last != scan) {
|
|
char store = *scan;
|
|
*scan = '\0';
|
|
StrAllocCat(pNewTagData,last);
|
|
*scan = store;
|
|
}
|
|
StrAllocCat(pNewTagData,pNew);
|
|
scan += pOldLen; // skip past prev string.
|
|
last = scan;
|
|
|
|
scan--; // To counteract following scan++.
|
|
}
|
|
}
|
|
|
|
scan++;
|
|
}
|
|
|
|
// flush last part.
|
|
if (last != scan) {
|
|
StrAllocCat(pNewTagData,last);
|
|
last = scan;
|
|
}
|
|
|
|
SetData(pNewTagData);
|
|
XP_FREEIF(pNewTagData);
|
|
}
|
|
|
|
|
|
PRIVATE
|
|
XP_Bool IsHTMLCommentTag(char* string) {
|
|
return string && string[0] == '!'
|
|
&& string[1] == '-'
|
|
&& string[2] == '-';
|
|
}
|
|
|
|
void CEditIconElement::SetSpoofData( PA_Tag* pTag ){
|
|
|
|
if( m_iconTag == EDT_ICON_UNSUPPORTED_TAG
|
|
|| m_iconTag == EDT_ICON_UNSUPPORTED_END_TAG ){
|
|
|
|
char *pTagString;
|
|
PA_LOCK(pTagString, char *, pTag->data );
|
|
|
|
char *pData = PR_smprintf( ppIconTags[m_iconTag],
|
|
edt_QuoteString( pTagString ));
|
|
|
|
XP_Bool bDimensionFound = FALSE;
|
|
XP_Bool bComment = m_originalTagType == P_UNKNOWN &&
|
|
IsHTMLCommentTag(pTagString);
|
|
|
|
if ( ! bComment ) {
|
|
int32 height;
|
|
int32 width;
|
|
XP_Bool bPercent;
|
|
|
|
edt_FetchDimension( pTag, PARAM_HEIGHT, &height, &bPercent, -1, TRUE, GetWinCSID() );
|
|
if( height != -1 ){
|
|
pData = PR_sprintf_append( pData, " HEIGHT=%d%s", height,
|
|
(bPercent ? "%" : "" ) );
|
|
bDimensionFound = TRUE;
|
|
}
|
|
|
|
edt_FetchDimension( pTag, PARAM_WIDTH, &width, &bPercent, -1, TRUE, GetWinCSID() );
|
|
if( width != -1 ){
|
|
pData = PR_sprintf_append( pData, " WIDTH=%d%s", width,
|
|
(bPercent ? "%" : "" ) );
|
|
bDimensionFound = TRUE;
|
|
}
|
|
}
|
|
|
|
if( bDimensionFound ){
|
|
pData = PR_sprintf_append(pData, " BORDER=2>");
|
|
}
|
|
else {
|
|
pData = PR_sprintf_append(pData, " BORDER=0 ALIGN=ABSBOTTOM>");
|
|
}
|
|
|
|
if( m_pSpoofData ){
|
|
free( m_pSpoofData );
|
|
}
|
|
m_pSpoofData = pData;
|
|
PA_UNLOCK(pTag->data);
|
|
}
|
|
else {
|
|
m_pSpoofData = XP_STRDUP( ppIconTags[m_iconTag] );
|
|
}
|
|
|
|
}
|
|
|
|
void CEditIconElement::SetSpoofData( char* pData ){
|
|
char *pNewData = PR_smprintf( ppIconTags[m_iconTag],
|
|
edt_QuoteString( pData ));
|
|
if( m_pSpoofData ){
|
|
XP_FREE( m_pSpoofData );
|
|
}
|
|
m_pSpoofData = pNewData;
|
|
}
|
|
|
|
void CEditIconElement::SetData( char *pData ){
|
|
// Since validater skips white space, so should SetData
|
|
while( *pData && IS_WHITE(*pData) ){
|
|
pData++;
|
|
}
|
|
|
|
// If you assert here, you aren't validating your tag.
|
|
if( pData[0] != '<' ) {
|
|
XP_ASSERT(FALSE);
|
|
return;
|
|
}
|
|
|
|
pData++;
|
|
|
|
if( *pData == '/' ){
|
|
pData++;
|
|
m_bEndTag = TRUE;
|
|
}
|
|
SetTagData( pData );
|
|
|
|
|
|
// Build up a tag that we can fetch parameter strings from.
|
|
PA_Tag *pTag = XP_NEW( PA_Tag );
|
|
XP_BZERO( pTag, sizeof( PA_Tag ) );
|
|
SetTagData( pTag, pData );
|
|
pTag->is_end = m_bEndTag;
|
|
|
|
SetSpoofData( pTag );
|
|
PA_FreeTag( pTag );
|
|
}
|
|
|
|
//
|
|
// We need to convert a tag handed to us by the parser into a tag we can
|
|
// display in the editor. In order to do this, we 'morph' the tag by swaping
|
|
// the contents with desired tag. We need an additional tag for swap space.
|
|
// All this is done to insure that the memory location of 'pTag' doesn't
|
|
// change. Perhaps this routine should be implemented at the CEditElement
|
|
// layer.
|
|
//
|
|
void CEditIconElement::MorphTag( PA_Tag *pTag ){
|
|
PA_Tag *pNewTag = TagOpen(0);
|
|
PA_Tag tempTag;
|
|
|
|
tempTag = *pTag;
|
|
*pTag = *pNewTag;
|
|
*pNewTag = tempTag;
|
|
|
|
PA_FreeTag( pNewTag );
|
|
}
|
|
|
|
XP_Bool CEditIconElement::IsUnknownHTML(){
|
|
return m_iconTag == EDT_ICON_UNSUPPORTED_TAG
|
|
|| m_iconTag == EDT_ICON_UNSUPPORTED_END_TAG;
|
|
}
|
|
|
|
XP_Bool CEditIconElement::IsComment(){
|
|
return IsUnknownHTML() && IsHTMLCommentTag(GetTagData());
|
|
}
|
|
|
|
XP_Bool CEditIconElement::IsComment(char* prefix){
|
|
if ( ! IsComment() ) return FALSE;
|
|
char* pData = GetTagData();
|
|
if ( ! prefix || ! pData ) return FALSE;
|
|
int32 prefixLen = XP_STRLEN(prefix);
|
|
int32 dataLen = XP_STRLEN(pData);
|
|
// Skip past "!--"
|
|
if ( dataLen <= 3 ) return FALSE;
|
|
dataLen -= 3;
|
|
pData += 3;
|
|
// Skip whitespace
|
|
while ( dataLen > 0 && *pData == ' ' ){
|
|
pData++;
|
|
dataLen--;
|
|
}
|
|
if ( prefixLen > dataLen ) return FALSE;
|
|
if ( XP_STRNCMP(prefix, pData, prefixLen) == 0 ){
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void CEditIconElement::SetSize(XP_Bool bWidthPercent, int32 iWidth,
|
|
XP_Bool bHeightPercent, int32 iHeight){
|
|
|
|
// Do NOT use TagOpen -- this gets the "m_pSpoofData" junk
|
|
PA_Tag *pTag = XP_NEW( PA_Tag );
|
|
XP_BZERO( pTag, sizeof( PA_Tag ) );
|
|
// This string has initial "<"
|
|
SetTagData( pTag, GetData() );
|
|
|
|
if( pTag ){
|
|
char *pWidth = 0;
|
|
char *pHeight = 0;
|
|
if( iWidth > 0 ){
|
|
pWidth = PR_sprintf_append( pWidth, "%ld%s", (long)iWidth, bWidthPercent ? "%%" : "" );
|
|
if( pWidth ){
|
|
edt_ReplaceParamValue( pTag, "WIDTH", pWidth, GetWinCSID());
|
|
}
|
|
}
|
|
if( iHeight > 0){
|
|
pHeight = PR_sprintf_append( pHeight, "%ld%s", (long)iHeight, bHeightPercent ? "%%" : "" );
|
|
if( pHeight ){
|
|
edt_ReplaceParamValue( pTag, "HEIGHT", pHeight, GetWinCSID());
|
|
}
|
|
}
|
|
if( pWidth || pHeight ){
|
|
SetData((char*)pTag->data);
|
|
if( pWidth ) XP_FREE(pWidth);
|
|
if( pHeight ) XP_FREE(pHeight);
|
|
}
|
|
PA_FREE(pTag);
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CEditTargetElement
|
|
//-----------------------------------------------------------------------------
|
|
CEditTargetElement::CEditTargetElement(CEditElement *pParent, PA_Tag* pTag, int16 csid)
|
|
:
|
|
CEditIconElement( pParent, EDT_ICON_NAMED_ANCHOR, pTag )
|
|
{
|
|
m_originalTagType = P_ANCHOR;
|
|
// To get the spoof tag set up correctly
|
|
EDT_TargetData* pData = GetData(csid);
|
|
SetData( pData );
|
|
FreeTargetData(pData);
|
|
}
|
|
|
|
CEditTargetElement::CEditTargetElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer)
|
|
:
|
|
CEditIconElement( pStreamIn, pBuffer )
|
|
{
|
|
}
|
|
|
|
void CEditTargetElement::StreamOut( IStreamOut *pOut){
|
|
CEditIconElement::StreamOut( pOut );
|
|
}
|
|
|
|
PA_Tag* CEditTargetElement::TagOpen( int iEditOffset ){
|
|
// We need to create the actual anchor tag set.
|
|
return CEditIconElement::TagOpen( iEditOffset );
|
|
}
|
|
|
|
void CEditTargetElement::SetName( char* pName, int16 csid ){
|
|
if ( pName ) {
|
|
EDT_TargetData* pData = GetData(csid);
|
|
if ( pData ) {
|
|
if ( pData->pName ) {
|
|
XP_FREE(pData->pName);
|
|
}
|
|
pData->pName = XP_STRDUP(pName);
|
|
}
|
|
SetData(pData);
|
|
FreeTargetData(pData);
|
|
}
|
|
}
|
|
|
|
void CEditTargetElement::SetData( EDT_TargetData *pData ){
|
|
char *pNew = 0;
|
|
if ( pData->pName) {
|
|
pNew = PR_sprintf_append( pNew, "NAME=%s ", edt_MakeParamString(pData->pName));
|
|
}
|
|
|
|
if( pData->pExtra ){
|
|
pNew = PR_sprintf_append( pNew, "%s ", pData->pExtra );
|
|
}
|
|
|
|
if( pNew ){
|
|
pNew = PR_sprintf_append( pNew, ">" );
|
|
}
|
|
SetTagData( pNew );
|
|
if ( pNew ) {
|
|
free(pNew);
|
|
}
|
|
// Set the spoof data for the icon.
|
|
CEditIconElement::SetSpoofData( pData->pName ? pData->pName : "" );
|
|
}
|
|
|
|
char* CEditTargetElement::GetName(){
|
|
char* pName = 0;
|
|
EDT_TargetData* pData = GetData();
|
|
if (pData && pData->pName) {
|
|
pName = XP_STRDUP(pData->pName);
|
|
}
|
|
FreeTargetData(pData);
|
|
return pName;
|
|
}
|
|
|
|
EDT_TargetData* CEditTargetElement::GetData(){
|
|
return GetData(GetWinCSID());
|
|
}
|
|
|
|
EDT_TargetData* CEditTargetElement::GetData(int16 csid){
|
|
EDT_TargetData *pRet;
|
|
// Get the actual tag data, not the faked up stuff at the Icon layer.
|
|
PA_Tag* pTag = CEditLeafElement::TagOpen(0);
|
|
pRet = ParseParams( pTag, csid );
|
|
PA_FreeTag( pTag );
|
|
return pRet;
|
|
}
|
|
|
|
|
|
static char *targetParams[] = {
|
|
PARAM_NAME,
|
|
0
|
|
};
|
|
|
|
EDT_TargetData* CEditTargetElement::ParseParams( PA_Tag *pTag, int16 csid ){
|
|
EDT_TargetData* pData = NewTargetData();
|
|
pData->pName = edt_FetchParamString( pTag, PARAM_NAME, csid );
|
|
pData->pExtra = edt_FetchParamExtras( pTag, targetParams, csid );
|
|
|
|
return pData;
|
|
}
|
|
|
|
EDT_TargetData* CEditTargetElement::NewTargetData(){
|
|
EDT_TargetData* pData = (EDT_TargetData*) XP_ALLOC(sizeof(EDT_TargetData));
|
|
if ( pData ) {
|
|
pData->pName = 0;
|
|
pData->pExtra = 0;
|
|
}
|
|
return pData;
|
|
}
|
|
|
|
void CEditTargetElement::FreeTargetData(EDT_TargetData* pData){
|
|
if ( pData ) {
|
|
if ( pData->pName ) {
|
|
XP_FREE(pData->pName);
|
|
}
|
|
if ( pData->pExtra ) {
|
|
XP_FREE(pData->pExtra);
|
|
}
|
|
XP_FREE(pData);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CEditBreakElement
|
|
//-----------------------------------------------------------------------------
|
|
|
|
CEditBreakElement::CEditBreakElement( CEditElement *pParent, PA_Tag* pTag, int16 /*csid*/ ):
|
|
CEditLeafElement( pParent, P_LINEBREAK ),
|
|
m_pLoLinefeed(0)
|
|
{
|
|
if( pTag ){
|
|
char *locked_buff;
|
|
|
|
PA_LOCK(locked_buff, char *, pTag->data );
|
|
if( locked_buff ){
|
|
SetTagData( locked_buff );
|
|
}
|
|
PA_UNLOCK(pTag->data);
|
|
}
|
|
}
|
|
|
|
CEditBreakElement::CEditBreakElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer):
|
|
CEditLeafElement(pStreamIn, pBuffer),
|
|
m_pLoLinefeed(0)
|
|
{
|
|
}
|
|
|
|
CEditBreakElement::~CEditBreakElement(){
|
|
DisconnectLayoutElements((LO_Element*) m_pLoLinefeed);
|
|
}
|
|
|
|
|
|
void CEditBreakElement::StreamOut( IStreamOut *pOut){
|
|
CEditLeafElement::StreamOut( pOut );
|
|
}
|
|
|
|
EEditElementType CEditBreakElement::GetElementType(){
|
|
return eBreakElement;
|
|
}
|
|
|
|
void CEditBreakElement::SetLayoutElement( intn iEditOffset, intn lo_type,
|
|
LO_Element* pLoElement ){
|
|
SetLayoutElementHelper(LO_LINEFEED, (LO_Element**) &m_pLoLinefeed,
|
|
iEditOffset, lo_type, pLoElement);
|
|
}
|
|
|
|
void CEditBreakElement::ResetLayoutElement( intn iEditOffset,
|
|
LO_Element* pLoElement ){
|
|
ResetLayoutElementHelper((LO_Element**) &m_pLoLinefeed,
|
|
iEditOffset, pLoElement);
|
|
}
|
|
|
|
LO_Element* CEditBreakElement::GetLayoutElement(){
|
|
return (LO_Element*)m_pLoLinefeed;
|
|
}
|
|
|
|
XP_Bool CEditBreakElement::GetLOElementAndOffset( ElementOffset iEditOffset, XP_Bool /* bStickyAfter */,
|
|
LO_Element*& pRetElement,
|
|
int& pLayoutOffset ){
|
|
pLayoutOffset = 0;
|
|
pRetElement = (LO_Element*)m_pLoLinefeed;
|
|
pLayoutOffset = iEditOffset;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// if we are within PREFORMAT (or the like) breaks are returns.
|
|
//
|
|
void CEditBreakElement::PrintOpen( CPrintState *ps ){
|
|
CEditElement *pPrev = PreviousLeafInContainer();
|
|
while( pPrev && pPrev->IsA(P_LINEBREAK) ){
|
|
pPrev = pPrev->PreviousLeafInContainer();
|
|
}
|
|
CEditElement *pNext = LeafInContainerAfter();
|
|
while( pNext && pNext->IsA(P_LINEBREAK) ){
|
|
pNext = pNext->LeafInContainerAfter();
|
|
}
|
|
#ifdef USE_SCRIPT
|
|
XP_Bool bScriptBefore = pPrev
|
|
&& pPrev->IsA(P_TEXT)
|
|
&& (pPrev->Text()->m_tf & (TF_SERVER|TF_SCRIPT|TF_STYLE));
|
|
XP_Bool bScriptAfter = pNext && pNext->IsA(P_TEXT) && (pNext->Text()->m_tf & (TF_SERVER|TF_SCRIPT|TF_STYLE));
|
|
|
|
if ( !bScriptBefore && bScriptAfter ){
|
|
// Don't print <BR>s before <SCRIPT> tags. This swallows both legal <BR> tags,
|
|
// and also swallows '\n' at the start of scripts. If we didn't do this,
|
|
// then we would turn <SCRIPT>\nFoo into <BR><SCRIPT>Foo.
|
|
// The case of a legitimate <BR> before a <SCRIPT> tag is much rarer than the
|
|
// case of a \n just after the <SCRIPT> tag. So we err on the side that lets
|
|
// more pages work.
|
|
// Take this code out when text styles are extended to all leaf elements.
|
|
return;
|
|
}
|
|
#endif
|
|
#ifdef USE_SCRIPT
|
|
if( InFormattedText()
|
|
|| bScriptBefore ){
|
|
#else
|
|
if( InFormattedText() ){
|
|
#endif
|
|
ps->m_pOut->Write( "\n", 1 );
|
|
ps->m_iCharPos = 0;
|
|
}
|
|
else {
|
|
CEditLeafElement::PrintOpen( ps );
|
|
// if we are the last break in a container, it is ingnored by the
|
|
// browser, so emit another one.
|
|
if( GetNextSibling() == 0 ){
|
|
ps->m_pOut->Write( "<BR>", 4 );
|
|
}
|
|
}
|
|
}
|
|
|
|
CEditInternalAnchorElement::CEditInternalAnchorElement(CEditElement *pParent)
|
|
: CEditLeafElement(pParent, P_UNKNOWN)
|
|
{
|
|
}
|
|
|
|
CEditInternalAnchorElement::~CEditInternalAnchorElement()
|
|
{
|
|
}
|
|
|
|
XP_Bool CEditInternalAnchorElement::Reduce( CEditBuffer* /* pBuffer */ ){
|
|
return FALSE;
|
|
}
|
|
|
|
XP_Bool CEditInternalAnchorElement::ShouldStreamSelf( CEditSelection& /*local*/, CEditSelection& /*selection*/){
|
|
return FALSE;
|
|
}
|
|
|
|
void CEditInternalAnchorElement::StreamOut( IStreamOut * /*pOut*/){
|
|
XP_ASSERT(FALSE);
|
|
}
|
|
|
|
void CEditInternalAnchorElement::SetLayoutElement( intn /*iEditOffset*/, intn /*lo_type*/,
|
|
LO_Element* /*pLoElement*/ ){
|
|
XP_ASSERT(FALSE);
|
|
}
|
|
|
|
void CEditInternalAnchorElement::ResetLayoutElement( intn /*iEditOffset*/,
|
|
LO_Element* /*pLoElement*/ ){
|
|
}
|
|
|
|
LO_Element* CEditInternalAnchorElement::GetLayoutElement(){
|
|
return NULL;
|
|
}
|
|
|
|
XP_Bool CEditInternalAnchorElement::GetLOElementAndOffset( ElementOffset /*iEditOffset*/, XP_Bool /*bEditStickyAfter*/,
|
|
LO_Element*& /*pRetElement*/,
|
|
int& /*pLayoutOffset*/ ){
|
|
return FALSE;
|
|
}
|
|
|
|
EEditElementType CEditInternalAnchorElement::GetElementType() {
|
|
return eInternalAnchorElement;
|
|
}
|
|
|
|
void CEditInternalAnchorElement::PrintOpen( CPrintState * /*pPrintState*/ ){
|
|
}
|
|
|
|
void CEditInternalAnchorElement::PrintEnd( CPrintState * /*pPrintState*/ ){
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// CEditEndContainerElement
|
|
//---------------------------------------------------------------------------
|
|
|
|
CEditEndContainerElement::CEditEndContainerElement(CEditElement *pParent) :
|
|
CEditContainerElement(pParent, (PA_Tag*)NULL, 0 /* Never used for an end container */, ED_ALIGN_LEFT)
|
|
{
|
|
}
|
|
|
|
void CEditEndContainerElement::StreamOut( IStreamOut * /*pOut*/ ) {
|
|
XP_ASSERT(FALSE);
|
|
}
|
|
|
|
XP_Bool CEditEndContainerElement::ShouldStreamSelf( CEditSelection& /* local */, CEditSelection& /* selection */ ) {
|
|
return FALSE;
|
|
}
|
|
XP_Bool CEditEndContainerElement::IsAcceptableChild(CEditElement& pChild){
|
|
return pChild.GetElementType() == eEndElement;
|
|
}
|
|
void CEditEndContainerElement::PrintOpen( CPrintState * /* pPrintState */ ){
|
|
}
|
|
void CEditEndContainerElement::PrintEnd( CPrintState * /* pPrintState */ ){
|
|
}
|
|
XP_Bool CEditEndContainerElement::IsEndContainer() {
|
|
return TRUE;
|
|
}
|
|
void CEditEndContainerElement::AdjustContainers( CEditBuffer* /* pBuffer */ ){
|
|
}
|
|
|
|
#endif //EDITOR
|