зеркало из https://github.com/mozilla/pjs.git
1807 строки
63 KiB
C++
1807 строки
63 KiB
C++
/* -*- Mode: C++ tab-width: 2 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.
|
|
*/
|
|
|
|
#include "nsTextEditor.h"
|
|
#include "nsIEditorSupport.h"
|
|
#include "nsEditorEventListeners.h"
|
|
#include "nsIEditProperty.h"
|
|
#include "nsEditProperty.h" // temporary, to get html atoms
|
|
|
|
#include "nsIStreamListener.h"
|
|
#include "nsIParser.h"
|
|
#include "nsParserCIID.h"
|
|
#include "nsIDocument.h"
|
|
#include "nsIHTMLContentSink.h"
|
|
#include "nsHTMLContentSinkStream.h"
|
|
#include "nsHTMLToTXTSinkStream.h"
|
|
#include "nsXIFDTD.h"
|
|
|
|
|
|
#include "nsIDOMDocument.h"
|
|
#include "nsIDOMEventReceiver.h"
|
|
#include "nsIDOMKeyListener.h"
|
|
#include "nsIDOMMouseListener.h"
|
|
#include "nsIDOMDragListener.h"
|
|
#include "nsIDOMSelection.h"
|
|
#include "nsIDOMRange.h"
|
|
#include "nsIDOMNodeList.h"
|
|
#include "nsIDOMCharacterData.h"
|
|
#include "nsIDOMElement.h"
|
|
#include "nsIDOMTextListener.h"
|
|
#include "nsEditorCID.h"
|
|
#include "nsISupportsArray.h"
|
|
#include "nsIEnumerator.h"
|
|
#include "nsIContentIterator.h"
|
|
#include "nsIContent.h"
|
|
#include "nsLayoutCID.h"
|
|
#include "nsIPresShell.h"
|
|
#include "nsIStyleContext.h"
|
|
|
|
// transactions the text editor knows how to build itself
|
|
#include "TransactionFactory.h"
|
|
#include "PlaceholderTxn.h"
|
|
#include "InsertTextTxn.h"
|
|
|
|
|
|
class nsIFrame;
|
|
|
|
#ifdef XP_UNIX
|
|
#include <strstream.h>
|
|
#endif
|
|
|
|
#ifdef XP_MAC
|
|
#include <sstream>
|
|
#include <string>
|
|
#endif
|
|
|
|
#ifdef XP_PC
|
|
#include <strstrea.h>
|
|
#endif
|
|
|
|
#include "nsIComponentManager.h"
|
|
#include "nsIServiceManager.h"
|
|
|
|
#include "nsTextEditRules.h"
|
|
|
|
|
|
static NS_DEFINE_IID(kIDOMEventReceiverIID, NS_IDOMEVENTRECEIVER_IID);
|
|
static NS_DEFINE_IID(kIDOMMouseListenerIID, NS_IDOMMOUSELISTENER_IID);
|
|
static NS_DEFINE_IID(kIDOMKeyListenerIID, NS_IDOMKEYLISTENER_IID);
|
|
static NS_DEFINE_IID(kIDOMTextListenerIID, NS_IDOMTEXTLISTENER_IID);
|
|
static NS_DEFINE_IID(kIDOMDragListenerIID, NS_IDOMDRAGLISTENER_IID);
|
|
static NS_DEFINE_IID(kIDOMSelectionListenerIID, NS_IDOMSELECTIONLISTENER_IID);
|
|
|
|
static NS_DEFINE_IID(kIEditPropertyIID, NS_IEDITPROPERTY_IID);
|
|
static NS_DEFINE_CID(kEditorCID, NS_EDITOR_CID);
|
|
static NS_DEFINE_IID(kIEditorIID, NS_IEDITOR_IID);
|
|
static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
|
|
static NS_DEFINE_IID(kITextEditorIID, NS_ITEXTEDITOR_IID);
|
|
static NS_DEFINE_CID(kTextEditorCID, NS_TEXTEDITOR_CID);
|
|
|
|
static NS_DEFINE_IID(kIContentIteratorIID, NS_ICONTENTITERTOR_IID);
|
|
static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID);
|
|
|
|
#ifdef NS_DEBUG
|
|
static PRBool gNoisy = PR_FALSE;
|
|
#else
|
|
static const PRBool gNoisy = PR_FALSE;
|
|
#endif
|
|
|
|
/* ---------- TypeInState implementation ---------- */
|
|
// most methods are defined inline in TypeInState.h
|
|
|
|
NS_IMPL_ADDREF(TypeInState)
|
|
|
|
NS_IMPL_RELEASE(TypeInState)
|
|
|
|
NS_IMETHODIMP
|
|
TypeInState::QueryInterface(REFNSIID aIID, void** aInstancePtr)
|
|
{
|
|
if (nsnull == aInstancePtr) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
if (aIID.Equals(kISupportsIID)) {
|
|
*aInstancePtr = (void*)(nsISupports*)this;
|
|
NS_ADDREF_THIS();
|
|
return NS_OK;
|
|
}
|
|
if (aIID.Equals(kIDOMSelectionListenerIID)) {
|
|
*aInstancePtr = (void*)(nsIDOMSelectionListener*)this;
|
|
NS_ADDREF_THIS();
|
|
return NS_OK;
|
|
}
|
|
return NS_NOINTERFACE;
|
|
}
|
|
|
|
TypeInState::~TypeInState()
|
|
{
|
|
};
|
|
|
|
NS_IMETHODIMP TypeInState::NotifySelectionChanged()
|
|
{
|
|
Reset();
|
|
return NS_OK;
|
|
};
|
|
|
|
|
|
/* ---------- nsTextEditor implementation ---------- */
|
|
|
|
nsTextEditor::nsTextEditor()
|
|
{
|
|
// Done in nsEditor
|
|
// NS_INIT_REFCNT();
|
|
mRules = nsnull;
|
|
nsEditProperty::InstanceInit();
|
|
}
|
|
|
|
nsTextEditor::~nsTextEditor()
|
|
{
|
|
//the autopointers will clear themselves up.
|
|
//but we need to also remove the listeners or we have a leak
|
|
nsCOMPtr<nsIDOMDocument> doc;
|
|
nsEditor::GetDocument(getter_AddRefs(doc));
|
|
if (doc)
|
|
{
|
|
nsCOMPtr<nsIDOMEventReceiver> erP;
|
|
nsresult result = doc->QueryInterface(kIDOMEventReceiverIID, getter_AddRefs(erP));
|
|
if (NS_SUCCEEDED(result) && erP)
|
|
{
|
|
if (mKeyListenerP) {
|
|
erP->RemoveEventListenerByIID(mKeyListenerP, kIDOMKeyListenerIID);
|
|
}
|
|
if (mMouseListenerP) {
|
|
erP->RemoveEventListenerByIID(mMouseListenerP, kIDOMMouseListenerIID);
|
|
}
|
|
|
|
if (mTextListenerP) {
|
|
erP->RemoveEventListenerByIID(mTextListenerP, kIDOMTextListenerIID);
|
|
}
|
|
|
|
if (mDragListenerP) {
|
|
erP->RemoveEventListenerByIID(mDragListenerP, kIDOMDragListenerIID);
|
|
}
|
|
|
|
}
|
|
else
|
|
NS_NOTREACHED("~nsTextEditor");
|
|
}
|
|
if (mRules) {
|
|
delete mRules;
|
|
}
|
|
nsEditProperty::InstanceShutdown();
|
|
}
|
|
|
|
// Adds appropriate AddRef, Release, and QueryInterface methods for derived class
|
|
//NS_IMPL_ISUPPORTS_INHERITED(nsTextEditor, nsEditor, nsITextEditor)
|
|
|
|
//NS_IMPL_ADDREF_INHERITED(Class, Super)
|
|
NS_IMETHODIMP_(nsrefcnt) nsTextEditor::AddRef(void)
|
|
{
|
|
return nsEditor::AddRef();
|
|
}
|
|
|
|
//NS_IMPL_RELEASE_INHERITED(Class, Super)
|
|
NS_IMETHODIMP_(nsrefcnt) nsTextEditor::Release(void)
|
|
{
|
|
return nsEditor::Release();
|
|
}
|
|
|
|
//NS_IMPL_QUERY_INTERFACE_INHERITED(Class, Super, AdditionalInterface)
|
|
NS_IMETHODIMP nsTextEditor::QueryInterface(REFNSIID aIID, void** aInstancePtr)
|
|
{
|
|
if (!aInstancePtr) return NS_ERROR_NULL_POINTER;
|
|
|
|
if (aIID.Equals(nsITextEditor::GetIID())) {
|
|
*aInstancePtr = NS_STATIC_CAST(nsITextEditor*, this);
|
|
NS_ADDREF_THIS();
|
|
return NS_OK;
|
|
}
|
|
return nsEditor::QueryInterface(aIID, aInstancePtr);
|
|
}
|
|
|
|
NS_IMETHODIMP nsTextEditor::Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell)
|
|
{
|
|
NS_PRECONDITION(nsnull!=aDoc && nsnull!=aPresShell, "bad arg");
|
|
nsresult result=NS_ERROR_NULL_POINTER;
|
|
if ((nsnull!=aDoc) && (nsnull!=aPresShell))
|
|
{
|
|
// Init the base editor
|
|
result = nsEditor::Init(aDoc, aPresShell);
|
|
if (NS_OK != result) { return result; }
|
|
|
|
nsCOMPtr<nsIDOMSelection>selection;
|
|
result = nsEditor::GetSelection(getter_AddRefs(selection));
|
|
if (NS_OK != result) { return result; }
|
|
if (selection) {
|
|
nsCOMPtr<nsIDOMSelectionListener>listener;
|
|
listener = do_QueryInterface(&mTypeInState);
|
|
if (listener) {
|
|
selection->AddSelectionListener(listener);
|
|
}
|
|
}
|
|
|
|
// Init the rules system
|
|
InitRules();
|
|
|
|
result = NS_NewEditorKeyListener(getter_AddRefs(mKeyListenerP), this);
|
|
if (NS_OK != result) {
|
|
return result;
|
|
}
|
|
result = NS_NewEditorMouseListener(getter_AddRefs(mMouseListenerP), this);
|
|
if (NS_OK != result) {
|
|
// drop the key listener if we couldn't get a mouse listener.
|
|
mKeyListenerP = do_QueryInterface(0);
|
|
return result;
|
|
}
|
|
|
|
result = NS_NewEditorTextListener(getter_AddRefs(mTextListenerP),this);
|
|
if (NS_OK !=result) {
|
|
// drop the key and mouse listeners
|
|
#ifdef DEBUG_TAGUE
|
|
printf("nsTextEditor.cpp: failed to get TextEvent Listener\n");
|
|
#endif
|
|
mMouseListenerP = do_QueryInterface(0);
|
|
mKeyListenerP = do_QueryInterface(0);
|
|
return result;
|
|
}
|
|
|
|
result = NS_NewEditorDragListener(getter_AddRefs(mDragListenerP), this);
|
|
if (NS_OK != result) {
|
|
//return result;
|
|
mMouseListenerP = do_QueryInterface(0);
|
|
mKeyListenerP = do_QueryInterface(0);
|
|
mTextListenerP = do_QueryInterface(0);
|
|
}
|
|
|
|
nsCOMPtr<nsIDOMEventReceiver> erP;
|
|
result = aDoc->QueryInterface(kIDOMEventReceiverIID, getter_AddRefs(erP));
|
|
if (NS_OK != result)
|
|
{
|
|
mKeyListenerP = do_QueryInterface(0);
|
|
mMouseListenerP = do_QueryInterface(0); //dont need these if we cant register them
|
|
mTextListenerP = do_QueryInterface(0);
|
|
mDragListenerP = do_QueryInterface(0); //dont need these if we cant register them
|
|
return result;
|
|
}
|
|
//cmanske: Shouldn't we check result from this?
|
|
erP->AddEventListenerByIID(mKeyListenerP, kIDOMKeyListenerIID);
|
|
//erP->AddEventListener(mDragListenerP, kIDOMDragListenerIID);
|
|
//erP->AddEventListener(mMouseListenerP, kIDOMMouseListenerIID);
|
|
|
|
erP->AddEventListenerByIID(mTextListenerP,kIDOMTextListenerIID);
|
|
|
|
result = NS_OK;
|
|
|
|
EnableUndo(PR_TRUE);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void nsTextEditor::InitRules()
|
|
{
|
|
// instantiate the rules for this text editor
|
|
// XXX: we should be told which set of rules to instantiate
|
|
mRules = new nsTextEditRules();
|
|
mRules->Init(this);
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsTextEditor::SetTextProperty(nsIAtom *aProperty)
|
|
{
|
|
if (!aProperty)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
nsresult result=NS_ERROR_NOT_INITIALIZED;
|
|
nsCOMPtr<nsIDOMSelection>selection;
|
|
result = nsEditor::GetSelection(getter_AddRefs(selection));
|
|
if ((NS_SUCCEEDED(result)) && selection)
|
|
{
|
|
PRBool isCollapsed;
|
|
selection->IsCollapsed(&isCollapsed);
|
|
if (PR_TRUE==isCollapsed)
|
|
{
|
|
// manipulating text attributes on a collapsed selection only sets state for the next text insertion
|
|
SetTypeInStateForProperty(mTypeInState, aProperty);
|
|
}
|
|
else
|
|
{
|
|
nsEditor::BeginTransaction();
|
|
nsCOMPtr<nsIEnumerator> enumerator;
|
|
enumerator = do_QueryInterface(selection);
|
|
if (enumerator)
|
|
{
|
|
enumerator->First();
|
|
nsISupports *currentItem;
|
|
result = enumerator->CurrentItem(¤tItem);
|
|
if ((NS_SUCCEEDED(result)) && (nsnull!=currentItem))
|
|
{
|
|
nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
|
|
nsCOMPtr<nsIDOMNode>commonParent;
|
|
result = range->GetCommonParent(getter_AddRefs(commonParent));
|
|
if ((NS_SUCCEEDED(result)) && commonParent)
|
|
{
|
|
PRInt32 startOffset, endOffset;
|
|
range->GetStartOffset(&startOffset);
|
|
range->GetEndOffset(&endOffset);
|
|
nsCOMPtr<nsIDOMNode> startParent; nsCOMPtr<nsIDOMNode> endParent;
|
|
range->GetStartParent(getter_AddRefs(startParent));
|
|
range->GetEndParent(getter_AddRefs(endParent));
|
|
if (startParent.get()==endParent.get())
|
|
{ // the range is entirely contained within a single text node
|
|
// commonParent==aStartParent, so get the "real" parent of the selection
|
|
startParent->GetParentNode(getter_AddRefs(commonParent));
|
|
result = SetTextPropertiesForNode(startParent, commonParent,
|
|
startOffset, endOffset,
|
|
aProperty);
|
|
}
|
|
else
|
|
{
|
|
nsCOMPtr<nsIDOMNode> startGrandParent;
|
|
startParent->GetParentNode(getter_AddRefs(startGrandParent));
|
|
nsCOMPtr<nsIDOMNode> endGrandParent;
|
|
endParent->GetParentNode(getter_AddRefs(endGrandParent));
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
PRBool canCollapseStyleNode = PR_FALSE;
|
|
if (endGrandParent.get()==startGrandParent.get())
|
|
{
|
|
result = IntermediateNodesAreInline(range, startParent, startOffset,
|
|
endParent, endOffset,
|
|
startGrandParent, canCollapseStyleNode);
|
|
}
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
if (PR_TRUE==canCollapseStyleNode)
|
|
{ // the range is between 2 nodes that have a common (immediate) grandparent,
|
|
// and any intermediate nodes are just inline style nodes
|
|
result = SetTextPropertiesForNodesWithSameParent(startParent,startOffset,
|
|
endParent, endOffset,
|
|
commonParent,
|
|
aProperty);
|
|
}
|
|
else
|
|
{ // the range is between 2 nodes that have no simple relationship
|
|
result = SetTextPropertiesForNodeWithDifferentParents(range,
|
|
startParent,startOffset,
|
|
endParent, endOffset,
|
|
commonParent,
|
|
aProperty);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (NS_SUCCEEDED(result))
|
|
{ // compute a range for the selection
|
|
// don't want to actually do anything with selection, because
|
|
// we are still iterating through it. Just want to create and remember
|
|
// an nsIDOMRange, and later add the range to the selection after clearing it.
|
|
// XXX: I'm blocked here because nsIDOMSelection doesn't provide a mechanism
|
|
// for setting a compound selection yet.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
nsEditor::EndTransaction();
|
|
}
|
|
if (NS_SUCCEEDED(result))
|
|
{ // set the selection
|
|
// XXX: can't do anything until I can create ranges
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
NS_IMETHODIMP nsTextEditor::GetTextProperty(nsIAtom *aProperty, PRBool &aFirst, PRBool &aAny, PRBool &aAll)
|
|
{
|
|
if (!aProperty)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
nsresult result=NS_ERROR_NOT_INITIALIZED;
|
|
aAny=PR_FALSE;
|
|
aAll=PR_TRUE;
|
|
aFirst=PR_FALSE;
|
|
PRBool first=PR_TRUE;
|
|
nsCOMPtr<nsIDOMSelection>selection;
|
|
result = nsEditor::GetSelection(getter_AddRefs(selection));
|
|
if ((NS_SUCCEEDED(result)) && selection)
|
|
{
|
|
nsCOMPtr<nsIEnumerator> enumerator;
|
|
enumerator = do_QueryInterface(selection);
|
|
if (enumerator)
|
|
{
|
|
enumerator->First();
|
|
nsISupports *currentItem;
|
|
result = enumerator->CurrentItem(¤tItem);
|
|
if ((NS_SUCCEEDED(result)) && currentItem)
|
|
{
|
|
nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
|
|
nsCOMPtr<nsIContentIterator> iter;
|
|
result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull,
|
|
kIContentIteratorIID,
|
|
getter_AddRefs(iter));
|
|
if ((NS_SUCCEEDED(result)) && iter)
|
|
{
|
|
iter->Init(range);
|
|
// loop through the content iterator for each content node
|
|
// for each text node:
|
|
// get the frame for the content, and from it the style context
|
|
// ask the style context about the property
|
|
nsCOMPtr<nsIContent> content;
|
|
result = iter->CurrentNode(getter_AddRefs(content));
|
|
while (NS_COMFALSE == iter->IsDone())
|
|
{
|
|
nsCOMPtr<nsIDOMCharacterData>text;
|
|
text = do_QueryInterface(content);
|
|
if (text)
|
|
{
|
|
nsCOMPtr<nsIDOMNode>node;
|
|
node = do_QueryInterface(content);
|
|
if (node)
|
|
{
|
|
PRBool isSet;
|
|
IsTextPropertySetByContent(node, aProperty, isSet);
|
|
if (PR_TRUE==first)
|
|
{
|
|
aFirst = isSet;
|
|
first = PR_FALSE;
|
|
}
|
|
if (PR_TRUE==isSet) {
|
|
aAny = PR_TRUE;
|
|
}
|
|
else {
|
|
aAll = PR_FALSE;
|
|
}
|
|
}
|
|
}
|
|
iter->Next();
|
|
result = iter->CurrentNode(getter_AddRefs(content));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void nsTextEditor::IsTextStyleSet(nsIStyleContext *aSC,
|
|
nsIAtom *aProperty,
|
|
PRBool &aIsSet) const
|
|
{
|
|
aIsSet = PR_FALSE;
|
|
if (aSC && aProperty)
|
|
{
|
|
nsStyleFont* font = (nsStyleFont*)aSC->GetStyleData(eStyleStruct_Font);
|
|
if (nsIEditProperty::i==aProperty)
|
|
{
|
|
aIsSet = PRBool(font->mFont.style & NS_FONT_STYLE_ITALIC);
|
|
}
|
|
else if (nsIEditProperty::b==aProperty)
|
|
{ // XXX: check this logic with Peter
|
|
aIsSet = PRBool(font->mFont.weight > NS_FONT_WEIGHT_NORMAL);
|
|
}
|
|
}
|
|
}
|
|
|
|
void nsTextEditor::IsTextPropertySetByContent(nsIDOMNode *aNode,
|
|
nsIAtom *aProperty,
|
|
PRBool &aIsSet) const
|
|
{
|
|
nsresult result;
|
|
aIsSet = PR_FALSE;
|
|
nsAutoString propName;
|
|
aProperty->ToString(propName);
|
|
nsCOMPtr<nsIDOMNode>parent;
|
|
result = aNode->GetParentNode(getter_AddRefs(parent));
|
|
while (NS_SUCCEEDED(result) && parent)
|
|
{
|
|
nsCOMPtr<nsIDOMElement>element;
|
|
element = do_QueryInterface(parent);
|
|
if (element)
|
|
{
|
|
nsString tag;
|
|
element->GetTagName(tag);
|
|
if (propName.Equals(tag))
|
|
{
|
|
aIsSet = PR_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
nsCOMPtr<nsIDOMNode>temp;
|
|
result = parent->GetParentNode(getter_AddRefs(temp));
|
|
if (NS_SUCCEEDED(result) && temp) {
|
|
parent = do_QueryInterface(temp);
|
|
}
|
|
else {
|
|
parent = do_QueryInterface(nsnull);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsTextEditor::RemoveTextProperty(nsIAtom *aProperty)
|
|
{
|
|
if (!aProperty)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
nsresult result=NS_ERROR_NOT_INITIALIZED;
|
|
nsCOMPtr<nsIDOMSelection>selection;
|
|
result = nsEditor::GetSelection(getter_AddRefs(selection));
|
|
if ((NS_SUCCEEDED(result)) && selection)
|
|
{
|
|
PRBool isCollapsed;
|
|
selection->IsCollapsed(&isCollapsed);
|
|
if (PR_TRUE==isCollapsed)
|
|
{
|
|
// manipulating text attributes on a collapsed selection only sets state for the next text insertion
|
|
SetTypeInStateForProperty(mTypeInState, aProperty);
|
|
}
|
|
else
|
|
{
|
|
nsEditor::BeginTransaction();
|
|
nsCOMPtr<nsIEnumerator> enumerator;
|
|
enumerator = do_QueryInterface(selection);
|
|
if (enumerator)
|
|
{
|
|
enumerator->First();
|
|
nsISupports *currentItem;
|
|
result = enumerator->CurrentItem(¤tItem);
|
|
if ((NS_SUCCEEDED(result)) && (nsnull!=currentItem))
|
|
{
|
|
nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
|
|
nsCOMPtr<nsIDOMNode>commonParent;
|
|
result = range->GetCommonParent(getter_AddRefs(commonParent));
|
|
if ((NS_SUCCEEDED(result)) && commonParent)
|
|
{
|
|
PRInt32 startOffset, endOffset;
|
|
range->GetStartOffset(&startOffset);
|
|
range->GetEndOffset(&endOffset);
|
|
nsCOMPtr<nsIDOMNode> startParent; nsCOMPtr<nsIDOMNode> endParent;
|
|
range->GetStartParent(getter_AddRefs(startParent));
|
|
range->GetEndParent(getter_AddRefs(endParent));
|
|
if (startParent.get()==endParent.get())
|
|
{ // the range is entirely contained within a single text node
|
|
// commonParent==aStartParent, so get the "real" parent of the selection
|
|
startParent->GetParentNode(getter_AddRefs(commonParent));
|
|
result = RemoveTextPropertiesForNode(startParent, commonParent,
|
|
startOffset, endOffset,
|
|
aProperty);
|
|
}
|
|
else
|
|
{
|
|
nsCOMPtr<nsIDOMNode> startGrandParent;
|
|
startParent->GetParentNode(getter_AddRefs(startGrandParent));
|
|
nsCOMPtr<nsIDOMNode> endGrandParent;
|
|
endParent->GetParentNode(getter_AddRefs(endGrandParent));
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
PRBool canCollapseStyleNode = PR_FALSE;
|
|
if (endGrandParent.get()==startGrandParent.get())
|
|
{
|
|
result = IntermediateNodesAreInline(range, startParent, startOffset,
|
|
endParent, endOffset,
|
|
startGrandParent, canCollapseStyleNode);
|
|
}
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
if (PR_TRUE==canCollapseStyleNode)
|
|
{ // the range is between 2 nodes that have a common (immediate) grandparent,
|
|
// and any intermediate nodes are just inline style nodes
|
|
result = RemoveTextPropertiesForNodesWithSameParent(startParent,startOffset,
|
|
endParent, endOffset,
|
|
commonParent,
|
|
aProperty);
|
|
}
|
|
else
|
|
{ // the range is between 2 nodes that have no simple relationship
|
|
result = RemoveTextPropertiesForNodeWithDifferentParents(range,
|
|
startParent,startOffset,
|
|
endParent, endOffset,
|
|
commonParent,
|
|
aProperty);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (NS_SUCCEEDED(result))
|
|
{ // compute a range for the selection
|
|
// don't want to actually do anything with selection, because
|
|
// we are still iterating through it. Just want to create and remember
|
|
// an nsIDOMRange, and later add the range to the selection after clearing it.
|
|
// XXX: I'm blocked here because nsIDOMSelection doesn't provide a mechanism
|
|
// for setting a compound selection yet.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
nsEditor::EndTransaction();
|
|
}
|
|
if (NS_SUCCEEDED(result))
|
|
{ // set the selection
|
|
// XXX: can't do anything until I can create ranges
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
NS_IMETHODIMP nsTextEditor::DeleteSelection(nsIEditor::Direction aDir)
|
|
{
|
|
if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
|
PRBool cancel= PR_FALSE;
|
|
|
|
nsresult result = nsEditor::BeginTransaction();
|
|
if (NS_FAILED(result)) { return result; }
|
|
|
|
// pre-process
|
|
nsEditor::GetSelection(getter_AddRefs(selection));
|
|
result = mRules->WillDoAction(nsTextEditRules::kDeleteSelection, selection, nsnull, &cancel);
|
|
if ((PR_FALSE==cancel) && (NS_SUCCEEDED(result)))
|
|
{
|
|
result = nsEditor::DeleteSelection(aDir);
|
|
// post-process
|
|
result = mRules->DidDoAction(nsTextEditRules::kDeleteSelection, selection, nsnull, result);
|
|
}
|
|
|
|
nsresult endTxnResult = nsEditor::EndTransaction(); // don't return this result!
|
|
NS_ASSERTION ((NS_SUCCEEDED(endTxnResult)), "bad end transaction result");
|
|
|
|
|
|
// XXXX: Horrible hack! We are doing this because
|
|
// of an error in Gecko which is not rendering the
|
|
// document after a change via the DOM - gpk 2/13/99
|
|
// BEGIN HACK!!!
|
|
HACKForceRedraw();
|
|
// END HACK
|
|
|
|
return result;
|
|
}
|
|
|
|
NS_IMETHODIMP nsTextEditor::InsertText(const nsString& aStringToInsert)
|
|
{
|
|
if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
|
PRBool cancel= PR_FALSE;
|
|
|
|
// pre-process
|
|
nsEditor::GetSelection(getter_AddRefs(selection));
|
|
PlaceholderTxn *placeholderTxn=nsnull;
|
|
nsresult result = mRules->WillDoAction(nsTextEditRules::kInsertText, selection, (void**)&placeholderTxn, &cancel);
|
|
if ((PR_FALSE==cancel) && (NS_SUCCEEDED(result)))
|
|
{
|
|
result = nsEditor::InsertText(aStringToInsert);
|
|
// post-process
|
|
result = mRules->DidDoAction(nsTextEditRules::kInsertText, selection, nsnull, result);
|
|
}
|
|
if (placeholderTxn)
|
|
placeholderTxn->SetAbsorb(PR_FALSE); // this ends the merging of txns into placeholderTxn
|
|
|
|
// BEGIN HACK!!!
|
|
HACKForceRedraw();
|
|
// END HACK
|
|
return result;
|
|
}
|
|
|
|
NS_IMETHODIMP nsTextEditor::InsertBreak()
|
|
{
|
|
// For plainttext just pass newlines through
|
|
nsAutoString key;
|
|
key += '\n';
|
|
return InsertText(key);
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsTextEditor::EnableUndo(PRBool aEnable)
|
|
{
|
|
return nsEditor::EnableUndo(aEnable);
|
|
}
|
|
|
|
NS_IMETHODIMP nsTextEditor::Undo(PRUint32 aCount)
|
|
{
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
|
PRBool cancel= PR_FALSE;
|
|
|
|
// pre-process
|
|
nsEditor::GetSelection(getter_AddRefs(selection));
|
|
nsresult result = mRules->WillDoAction(nsTextEditRules::kUndo, selection, nsnull, &cancel);
|
|
if ((PR_FALSE==cancel) && (NS_SUCCEEDED(result)))
|
|
{
|
|
result = nsEditor::Undo(aCount);
|
|
nsEditor::GetSelection(getter_AddRefs(selection));
|
|
result = mRules->DidDoAction(nsTextEditRules::kUndo, selection, nsnull, result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
NS_IMETHODIMP nsTextEditor::CanUndo(PRBool &aIsEnabled, PRBool &aCanUndo)
|
|
{
|
|
return nsEditor::CanUndo(aIsEnabled, aCanUndo);
|
|
}
|
|
|
|
NS_IMETHODIMP nsTextEditor::Redo(PRUint32 aCount)
|
|
{
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
|
PRBool cancel= PR_FALSE;
|
|
|
|
// pre-process
|
|
nsEditor::GetSelection(getter_AddRefs(selection));
|
|
nsresult result = mRules->WillDoAction(nsTextEditRules::kRedo, selection, nsnull, &cancel);
|
|
if ((PR_FALSE==cancel) && (NS_SUCCEEDED(result)))
|
|
{
|
|
result = nsEditor::Redo(aCount);
|
|
nsEditor::GetSelection(getter_AddRefs(selection));
|
|
result = mRules->DidDoAction(nsTextEditRules::kRedo, selection, nsnull, result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
NS_IMETHODIMP nsTextEditor::CanRedo(PRBool &aIsEnabled, PRBool &aCanRedo)
|
|
{
|
|
return nsEditor::CanRedo(aIsEnabled, aCanRedo);
|
|
}
|
|
|
|
NS_IMETHODIMP nsTextEditor::BeginTransaction()
|
|
{
|
|
return nsEditor::BeginTransaction();
|
|
}
|
|
|
|
NS_IMETHODIMP nsTextEditor::EndTransaction()
|
|
{
|
|
return nsEditor::EndTransaction();
|
|
}
|
|
|
|
NS_IMETHODIMP nsTextEditor::MoveSelectionUp(nsIAtom *aIncrement, PRBool aExtendSelection)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP nsTextEditor::MoveSelectionDown(nsIAtom *aIncrement, PRBool aExtendSelection)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP nsTextEditor::MoveSelectionNext(nsIAtom *aIncrement, PRBool aExtendSelection)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP nsTextEditor::MoveSelectionPrevious(nsIAtom *aIncrement, PRBool aExtendSelection)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP nsTextEditor::SelectNext(nsIAtom *aIncrement, PRBool aExtendSelection)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP nsTextEditor::SelectPrevious(nsIAtom *aIncrement, PRBool aExtendSelection)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP nsTextEditor::SelectAll()
|
|
{
|
|
return nsEditor::SelectAll();
|
|
}
|
|
|
|
NS_IMETHODIMP nsTextEditor::ScrollUp(nsIAtom *aIncrement)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP nsTextEditor::ScrollDown(nsIAtom *aIncrement)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP nsTextEditor::ScrollIntoView(PRBool aScrollToBegin)
|
|
{
|
|
return nsEditor::ScrollIntoView(aScrollToBegin);
|
|
}
|
|
|
|
NS_IMETHODIMP nsTextEditor::Cut()
|
|
{
|
|
return nsEditor::Cut();
|
|
}
|
|
|
|
NS_IMETHODIMP nsTextEditor::Copy()
|
|
{
|
|
return nsEditor::Copy();
|
|
}
|
|
|
|
NS_IMETHODIMP nsTextEditor::Paste()
|
|
{
|
|
return nsEditor::Paste();
|
|
}
|
|
|
|
NS_IMETHODIMP nsTextEditor::Insert(nsString& aInputString)
|
|
{
|
|
printf("nsTextEditor::Insert not yet implemented\n");
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
#ifdef XP_MAC
|
|
static void WriteFromStringstream(stringstream& aIn, nsString& aOutputString)
|
|
{
|
|
string theString = aIn.str();
|
|
|
|
aOutputString.SetLength(0); // empty the string
|
|
aOutputString += theString.data();
|
|
aOutputString.SetLength(theString.length()); // make sure it's terminated
|
|
|
|
/* relace LF with CR. Don't do this here, because strings passed out
|
|
to JavaScript need LF termination.
|
|
PRUnichar lineFeed = '\n';
|
|
PRUnichar carriageReturn = '\r';
|
|
aOutputString.ReplaceChar(lineFeed, carriageReturn);
|
|
*/
|
|
}
|
|
#else
|
|
static void WriteFromOstrstream(ostrstream& aIn, nsString& aOutputString)
|
|
{
|
|
char* strData = aIn.str(); // get a copy of the buffer (unterminated)
|
|
|
|
aOutputString.SetLength(0); // empty the string
|
|
aOutputString += strData;
|
|
aOutputString.SetLength(aIn.pcount()); // terminate
|
|
|
|
// in ostrstreams if you call the str() function
|
|
// then you are responsible for deleting the string
|
|
delete [] strData;
|
|
}
|
|
#endif
|
|
|
|
NS_IMETHODIMP nsTextEditor::OutputText(nsString& aOutputString)
|
|
{
|
|
#ifdef XP_MAC
|
|
stringstream outStream;
|
|
#else
|
|
ostrstream outStream;
|
|
#endif
|
|
|
|
nsresult rv = NS_ERROR_FAILURE;
|
|
nsIPresShell* shell = nsnull;
|
|
|
|
GetPresShell(&shell);
|
|
if (nsnull != shell) {
|
|
nsCOMPtr<nsIDocument> doc;
|
|
shell->GetDocument(getter_AddRefs(doc));
|
|
if (doc) {
|
|
nsString buffer;
|
|
|
|
doc->CreateXIF(buffer);
|
|
|
|
nsIParser* parser;
|
|
|
|
static NS_DEFINE_IID(kCParserIID, NS_IPARSER_IID);
|
|
static NS_DEFINE_IID(kCParserCID, NS_PARSER_IID);
|
|
|
|
rv = nsComponentManager::CreateInstance(kCParserCID,
|
|
nsnull,
|
|
kCParserIID,
|
|
(void **)&parser);
|
|
|
|
if (NS_OK == rv) {
|
|
nsIHTMLContentSink* sink = nsnull;
|
|
|
|
rv = NS_New_HTMLToTXT_SinkStream(&sink);
|
|
if (NS_OK == rv) {
|
|
// what't this cast doing here, Greg?
|
|
((nsHTMLContentSinkStream*)sink)->SetOutputStream(outStream);
|
|
|
|
if (NS_OK == rv) {
|
|
parser->SetContentSink(sink);
|
|
|
|
nsIDTD* dtd = nsnull;
|
|
rv = NS_NewXIFDTD(&dtd);
|
|
if (NS_OK == rv) {
|
|
parser->RegisterDTD(dtd);
|
|
parser->Parse(buffer, 0, "text/xif",PR_FALSE,PR_TRUE);
|
|
}
|
|
#ifdef XP_MAC
|
|
WriteFromStringstream(outStream, aOutputString);
|
|
#else
|
|
WriteFromOstrstream(outStream, aOutputString);
|
|
#endif
|
|
|
|
NS_IF_RELEASE(dtd);
|
|
NS_IF_RELEASE(sink);
|
|
}
|
|
}
|
|
NS_RELEASE(parser);
|
|
}
|
|
}
|
|
NS_RELEASE(shell);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsTextEditor::OutputHTML(nsString& aOutputString)
|
|
{
|
|
#ifdef XP_MAC
|
|
stringstream outStream;
|
|
#else
|
|
ostrstream outStream;
|
|
#endif
|
|
|
|
nsresult rv = NS_ERROR_FAILURE;
|
|
nsIPresShell* shell = nsnull;
|
|
|
|
GetPresShell(&shell);
|
|
if (nsnull != shell) {
|
|
nsCOMPtr<nsIDocument> doc;
|
|
shell->GetDocument(getter_AddRefs(doc));
|
|
if (doc) {
|
|
nsString buffer;
|
|
|
|
doc->CreateXIF(buffer);
|
|
|
|
nsIParser* parser;
|
|
|
|
static NS_DEFINE_IID(kCParserIID, NS_IPARSER_IID);
|
|
static NS_DEFINE_IID(kCParserCID, NS_PARSER_IID);
|
|
|
|
rv = nsComponentManager::CreateInstance(kCParserCID,
|
|
nsnull,
|
|
kCParserIID,
|
|
(void **)&parser);
|
|
|
|
if (NS_OK == rv) {
|
|
nsIHTMLContentSink* sink = nsnull;
|
|
|
|
rv = NS_New_HTML_ContentSinkStream(&sink);
|
|
|
|
if (NS_OK == rv) {
|
|
((nsHTMLContentSinkStream*)sink)->SetOutputStream(outStream);
|
|
|
|
if (NS_OK == rv) {
|
|
parser->SetContentSink(sink);
|
|
|
|
nsIDTD* dtd = nsnull;
|
|
rv = NS_NewXIFDTD(&dtd);
|
|
if (NS_OK == rv) {
|
|
parser->RegisterDTD(dtd);
|
|
parser->Parse(buffer, 0, "text/xif",PR_FALSE,PR_TRUE);
|
|
}
|
|
#ifdef XP_MAC
|
|
WriteFromStringstream(outStream, aOutputString);
|
|
#else
|
|
WriteFromOstrstream(outStream, aOutputString);
|
|
#endif
|
|
NS_IF_RELEASE(dtd);
|
|
NS_IF_RELEASE(sink);
|
|
}
|
|
}
|
|
NS_RELEASE(parser);
|
|
}
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsTextEditor::SetTextPropertiesForNode(nsIDOMNode *aNode,
|
|
nsIDOMNode *aParent,
|
|
PRInt32 aStartOffset,
|
|
PRInt32 aEndOffset,
|
|
nsIAtom *aPropName)
|
|
{
|
|
nsresult result=NS_OK;
|
|
nsCOMPtr<nsIDOMCharacterData>nodeAsChar;
|
|
nodeAsChar = do_QueryInterface(aNode);
|
|
if (!nodeAsChar)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
PRBool textPropertySet;
|
|
IsTextPropertySetByContent(aNode, aPropName, textPropertySet);
|
|
if (PR_FALSE==textPropertySet)
|
|
{
|
|
PRUint32 count;
|
|
nodeAsChar->GetLength(&count);
|
|
|
|
nsCOMPtr<nsIDOMNode>newTextNode; // this will be the text node we move into the new style node
|
|
if (aStartOffset!=0)
|
|
{
|
|
result = nsEditor::SplitNode(aNode, aStartOffset, getter_AddRefs(newTextNode));
|
|
}
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
if (aEndOffset!=(PRInt32)count)
|
|
{
|
|
result = nsEditor::SplitNode(aNode, aEndOffset-aStartOffset, getter_AddRefs(newTextNode));
|
|
}
|
|
else
|
|
{
|
|
newTextNode = do_QueryInterface(aNode);
|
|
}
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
nsAutoString tag;
|
|
aPropName->ToString(tag);
|
|
PRInt32 offsetInParent;
|
|
result = nsIEditorSupport::GetChildOffset(aNode, aParent, offsetInParent);
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
nsCOMPtr<nsIDOMNode>newStyleNode;
|
|
result = nsEditor::CreateNode(tag, aParent, offsetInParent, getter_AddRefs(newStyleNode));
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
result = nsEditor::DeleteNode(newTextNode);
|
|
if (NS_SUCCEEDED(result)) {
|
|
result = nsEditor::InsertNode(newTextNode, newStyleNode, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// XXX: WRITE ME!
|
|
// We can do this content based or style based
|
|
// content-based has the advantage of being "correct" regardless of the style sheet
|
|
// it has the disadvantage of not supporting nonHTML tags
|
|
NS_IMETHODIMP nsTextEditor::IsNodeInline(nsIDOMNode *aNode, PRBool &aIsInline) const
|
|
{
|
|
// this is a content-based implementation
|
|
if (!aNode) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
nsresult result;
|
|
aIsInline = PR_FALSE;
|
|
nsCOMPtr<nsIDOMElement>element;
|
|
element = do_QueryInterface(aNode);
|
|
if (element)
|
|
{
|
|
nsAutoString tag;
|
|
result = element->GetTagName(tag);
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
nsIAtom *tagAtom = NS_NewAtom(tag);
|
|
if (!tagAtom) { return NS_ERROR_NULL_POINTER; }
|
|
if (tagAtom==nsIEditProperty::a ||
|
|
tagAtom==nsIEditProperty::b ||
|
|
tagAtom==nsIEditProperty::big ||
|
|
tagAtom==nsIEditProperty::font ||
|
|
tagAtom==nsIEditProperty::i ||
|
|
tagAtom==nsIEditProperty::span ||
|
|
tagAtom==nsIEditProperty::small ||
|
|
tagAtom==nsIEditProperty::strike ||
|
|
tagAtom==nsIEditProperty::sub ||
|
|
tagAtom==nsIEditProperty::sup ||
|
|
tagAtom==nsIEditProperty::tt ||
|
|
tagAtom==nsIEditProperty::u )
|
|
{
|
|
aIsInline = PR_TRUE;
|
|
}
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTextEditor::IntermediateNodesAreInline(nsIDOMRange *aRange,
|
|
nsIDOMNode *aStartNode,
|
|
PRInt32 aStartOffset,
|
|
nsIDOMNode *aEndNode,
|
|
PRInt32 aEndOffset,
|
|
nsIDOMNode *aParent,
|
|
PRBool &aResult) const
|
|
{
|
|
aResult = PR_TRUE; // init out param. we assume the condition is true unless we find a node that violates it
|
|
if (!aStartNode || !aEndNode || !aParent) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
nsCOMPtr<nsIContentIterator>iter;
|
|
nsresult result;
|
|
result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull,
|
|
kIContentIteratorIID, getter_AddRefs(iter));
|
|
//XXX: maybe CreateInstance is expensive, and I should keep around a static iter?
|
|
// as long as this method can't be called recursively or re-entrantly!
|
|
|
|
if ((NS_SUCCEEDED(result)) && iter)
|
|
{
|
|
nsCOMPtr<nsIContent>startContent;
|
|
startContent = do_QueryInterface(aStartNode);
|
|
nsCOMPtr<nsIContent>endContent;
|
|
endContent = do_QueryInterface(aEndNode);
|
|
if (startContent && endContent)
|
|
{
|
|
iter->Init(aRange);
|
|
nsCOMPtr<nsIContent> content;
|
|
iter->CurrentNode(getter_AddRefs(content));
|
|
while (NS_COMFALSE == iter->IsDone())
|
|
{
|
|
if ((content.get() != startContent.get()) &&
|
|
(content.get() != endContent.get()))
|
|
{
|
|
nsCOMPtr<nsIDOMNode>currentNode;
|
|
currentNode = do_QueryInterface(content);
|
|
PRBool isInline=PR_FALSE;
|
|
IsNodeInline(currentNode, isInline);
|
|
if (PR_FALSE==isInline)
|
|
{
|
|
nsCOMPtr<nsIDOMCharacterData>nodeAsText;
|
|
nodeAsText = do_QueryInterface(currentNode);
|
|
if (!nodeAsText) // text nodes don't count in this check, so ignore them
|
|
{
|
|
aResult = PR_FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/* do not check result here, and especially do not return the result code.
|
|
* we rely on iter->IsDone to tell us when the iteration is complete
|
|
*/
|
|
iter->Next();
|
|
iter->CurrentNode(getter_AddRefs(content));
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* this should only get called if the only intervening nodes are inline style nodes */
|
|
NS_IMETHODIMP
|
|
nsTextEditor::SetTextPropertiesForNodesWithSameParent(nsIDOMNode *aStartNode,
|
|
PRInt32 aStartOffset,
|
|
nsIDOMNode *aEndNode,
|
|
PRInt32 aEndOffset,
|
|
nsIDOMNode *aParent,
|
|
nsIAtom *aPropName)
|
|
{
|
|
nsresult result=NS_OK;
|
|
PRBool textPropertySet;
|
|
IsTextPropertySetByContent(aStartNode, aPropName, textPropertySet);
|
|
if (PR_FALSE==textPropertySet)
|
|
{
|
|
nsCOMPtr<nsIDOMNode>newLeftTextNode; // this will be the middle text node
|
|
if (0!=aStartOffset) {
|
|
result = nsEditor::SplitNode(aStartNode, aStartOffset, getter_AddRefs(newLeftTextNode));
|
|
}
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
nsCOMPtr<nsIDOMCharacterData>endNodeAsChar;
|
|
endNodeAsChar = do_QueryInterface(aEndNode);
|
|
if (!endNodeAsChar)
|
|
return NS_ERROR_FAILURE;
|
|
PRUint32 count;
|
|
endNodeAsChar->GetLength(&count);
|
|
nsCOMPtr<nsIDOMNode>newRightTextNode; // this will be the middle text node
|
|
if ((PRInt32)count!=aEndOffset) {
|
|
result = nsEditor::SplitNode(aEndNode, aEndOffset, getter_AddRefs(newRightTextNode));
|
|
}
|
|
else {
|
|
newRightTextNode = do_QueryInterface(aEndNode);
|
|
}
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
PRInt32 offsetInParent;
|
|
if (newLeftTextNode) {
|
|
result = nsIEditorSupport::GetChildOffset(newLeftTextNode, aParent, offsetInParent);
|
|
}
|
|
else {
|
|
offsetInParent = -1; // relies on +1 below in call to CreateNode
|
|
}
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
nsAutoString tag;
|
|
aPropName->ToString(tag);
|
|
// create the new style node, which will be the new parent for the selected nodes
|
|
nsCOMPtr<nsIDOMNode>newStyleNode;
|
|
result = nsEditor::CreateNode(tag, aParent, offsetInParent+1, getter_AddRefs(newStyleNode));
|
|
if (NS_SUCCEEDED(result))
|
|
{ // move the right half of the start node into the new style node
|
|
nsCOMPtr<nsIDOMNode>intermediateNode;
|
|
result = aStartNode->GetNextSibling(getter_AddRefs(intermediateNode));
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
result = nsEditor::DeleteNode(aStartNode);
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
PRInt32 childIndex=0;
|
|
result = nsEditor::InsertNode(aStartNode, newStyleNode, childIndex);
|
|
childIndex++;
|
|
if (NS_SUCCEEDED(result))
|
|
{ // move all the intermediate nodes into the new style node
|
|
nsCOMPtr<nsIDOMNode>nextSibling;
|
|
while (intermediateNode.get() != aEndNode)
|
|
{
|
|
if (!intermediateNode)
|
|
result = NS_ERROR_NULL_POINTER;
|
|
if (NS_FAILED(result)) {
|
|
break;
|
|
}
|
|
// get the next sibling before moving the current child!!!
|
|
intermediateNode->GetNextSibling(getter_AddRefs(nextSibling));
|
|
result = nsEditor::DeleteNode(intermediateNode);
|
|
if (NS_SUCCEEDED(result)) {
|
|
result = nsEditor::InsertNode(intermediateNode, newStyleNode, childIndex);
|
|
childIndex++;
|
|
}
|
|
intermediateNode = do_QueryInterface(nextSibling);
|
|
}
|
|
if (NS_SUCCEEDED(result))
|
|
{ // move the left half of the end node into the new style node
|
|
result = nsEditor::DeleteNode(newRightTextNode);
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
result = nsEditor::InsertNode(newRightTextNode, newStyleNode, childIndex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* this wraps every selected text node in a new inline style node if needed
|
|
the text nodes are treated as being unique -- each needs it's own style node
|
|
if the style is not already present.
|
|
each action has immediate effect on the content tree and resolved style, so
|
|
doing outermost text nodes first removes the need for interior style nodes in some cases.
|
|
XXX: need to code test to see if new style node is needed
|
|
*/
|
|
NS_IMETHODIMP
|
|
nsTextEditor::SetTextPropertiesForNodeWithDifferentParents(nsIDOMRange *aRange,
|
|
nsIDOMNode *aStartNode,
|
|
PRInt32 aStartOffset,
|
|
nsIDOMNode *aEndNode,
|
|
PRInt32 aEndOffset,
|
|
nsIDOMNode *aParent,
|
|
nsIAtom *aPropName)
|
|
{
|
|
nsresult result=NS_OK;
|
|
if (!aRange || !aStartNode || !aEndNode || !aParent || !aPropName)
|
|
return NS_ERROR_NULL_POINTER;
|
|
// create a style node for the text in the start parent
|
|
nsCOMPtr<nsIDOMNode>parent;
|
|
result = aStartNode->GetParentNode(getter_AddRefs(parent));
|
|
if (NS_FAILED(result)) {
|
|
return result;
|
|
}
|
|
|
|
// create style nodes for all the content between the start and end nodes
|
|
nsCOMPtr<nsIContentIterator>iter;
|
|
result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull,
|
|
kIContentIteratorIID, getter_AddRefs(iter));
|
|
if ((NS_SUCCEEDED(result)) && iter)
|
|
{
|
|
nsCOMPtr<nsIContent>startContent;
|
|
startContent = do_QueryInterface(aStartNode);
|
|
nsCOMPtr<nsIContent>endContent;
|
|
endContent = do_QueryInterface(aEndNode);
|
|
if (startContent && endContent)
|
|
{
|
|
iter->Init(aRange);
|
|
nsCOMPtr<nsIContent> content;
|
|
iter->CurrentNode(getter_AddRefs(content));
|
|
nsAutoString tag;
|
|
aPropName->ToString(tag);
|
|
while (NS_COMFALSE == iter->IsDone())
|
|
{
|
|
if ((content.get() != startContent.get()) &&
|
|
(content.get() != endContent.get()))
|
|
{
|
|
nsCOMPtr<nsIDOMCharacterData>charNode;
|
|
charNode = do_QueryInterface(content);
|
|
if (charNode)
|
|
{
|
|
// only want to wrap the text node in a new style node if it doesn't already have that style
|
|
nsCOMPtr<nsIDOMNode>node;
|
|
node = do_QueryInterface(content);
|
|
PRBool textPropertySet;
|
|
IsTextPropertySetByContent(node, aPropName, textPropertySet);
|
|
if (PR_FALSE==textPropertySet)
|
|
{
|
|
nsCOMPtr<nsIDOMNode>parent;
|
|
charNode->GetParentNode(getter_AddRefs(parent));
|
|
if (!parent) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
nsCOMPtr<nsIContent>parentContent;
|
|
parentContent = do_QueryInterface(parent);
|
|
|
|
PRInt32 offsetInParent;
|
|
parentContent->IndexOf(content, offsetInParent);
|
|
|
|
nsCOMPtr<nsIDOMNode>newStyleNode;
|
|
result = nsEditor::CreateNode(tag, parent, offsetInParent, getter_AddRefs(newStyleNode));
|
|
if (NS_SUCCEEDED(result) && newStyleNode) {
|
|
nsCOMPtr<nsIDOMNode>contentNode;
|
|
contentNode = do_QueryInterface(content);
|
|
result = nsEditor::DeleteNode(contentNode);
|
|
if (NS_SUCCEEDED(result)) {
|
|
result = nsEditor::InsertNode(contentNode, newStyleNode, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// note we don't check the result, we just rely on iter->IsDone
|
|
iter->Next();
|
|
result = iter->CurrentNode(getter_AddRefs(content));
|
|
}
|
|
}
|
|
}
|
|
|
|
nsCOMPtr<nsIDOMCharacterData>nodeAsChar;
|
|
nodeAsChar = do_QueryInterface(aStartNode);
|
|
if (!nodeAsChar)
|
|
return NS_ERROR_FAILURE;
|
|
PRUint32 count;
|
|
nodeAsChar->GetLength(&count);
|
|
result = SetTextPropertiesForNode(aStartNode, parent, aStartOffset, count, aPropName);
|
|
|
|
// create a style node for the text in the end parent
|
|
result = aEndNode->GetParentNode(getter_AddRefs(parent));
|
|
if (NS_FAILED(result)) {
|
|
return result;
|
|
}
|
|
nodeAsChar = do_QueryInterface(aEndNode);
|
|
if (!nodeAsChar)
|
|
return NS_ERROR_FAILURE;
|
|
nodeAsChar->GetLength(&count);
|
|
result = SetTextPropertiesForNode(aEndNode, parent, 0, aEndOffset, aPropName);
|
|
|
|
return result;
|
|
}
|
|
|
|
NS_IMETHODIMP nsTextEditor::RemoveTextPropertiesForNode(nsIDOMNode *aNode,
|
|
nsIDOMNode *aParent,
|
|
PRInt32 aStartOffset,
|
|
PRInt32 aEndOffset,
|
|
nsIAtom *aPropName)
|
|
{
|
|
nsresult result=NS_OK;
|
|
nsCOMPtr<nsIDOMCharacterData>nodeAsChar;
|
|
nodeAsChar = do_QueryInterface(aNode);
|
|
if (!nodeAsChar)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
PRBool textPropertySet;
|
|
IsTextPropertySetByContent(aNode, aPropName, textPropertySet);
|
|
if (PR_TRUE==textPropertySet)
|
|
{
|
|
PRUint32 count;
|
|
nodeAsChar->GetLength(&count);
|
|
// split the node, and all parent nodes up to the style node
|
|
// then promote the selected content to the parent of the style node
|
|
nsCOMPtr<nsIDOMNode>newLeftNode; // this will be the leftmost node,
|
|
// the node being split will be rightmost
|
|
if (0!=aStartOffset) {
|
|
printf("* splitting text node %p at %d\n", aNode, aStartOffset);
|
|
result = nsEditor::SplitNode(aNode, aStartOffset, getter_AddRefs(newLeftNode));
|
|
printf("* split created left node %p\n", newLeftNode.get());
|
|
if (gNoisy) {DebugDumpContent(); } // DEBUG
|
|
}
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
nsCOMPtr<nsIDOMNode>newMiddleNode; // this will be the middle node
|
|
if ((PRInt32)count!=aEndOffset) {
|
|
printf("* splitting text node (right node) %p at %d\n", aNode, aEndOffset-aStartOffset);
|
|
result = nsEditor::SplitNode(aNode, aEndOffset-aStartOffset, getter_AddRefs(newMiddleNode));
|
|
printf("* split created middle node %p\n", newMiddleNode.get());
|
|
if (gNoisy) {DebugDumpContent(); } // DEBUG
|
|
}
|
|
else {
|
|
printf("* no need to split text node\n");
|
|
newMiddleNode = do_QueryInterface(aNode);
|
|
}
|
|
NS_ASSERTION(newMiddleNode, "no middle node created");
|
|
// now that the text node is split, split parent nodes until we get to the style node
|
|
nsCOMPtr<nsIDOMNode>parent;
|
|
parent = do_QueryInterface(aParent); // we know this has to succeed, no need to check
|
|
if (NS_SUCCEEDED(result) && newMiddleNode)
|
|
{
|
|
// split every ancestor until we find the node that is giving us the style we want to remove
|
|
// then split the style node and promote the selected content to the style node's parent
|
|
while (NS_SUCCEEDED(result) && parent)
|
|
{
|
|
printf("* looking at parent %p\n", parent);
|
|
// get the tag from parent and see if we're done
|
|
nsCOMPtr<nsIDOMNode>temp;
|
|
nsCOMPtr<nsIDOMElement>element;
|
|
element = do_QueryInterface(parent);
|
|
if (element)
|
|
{
|
|
nsAutoString tag;
|
|
result = element->GetTagName(tag);
|
|
printf("* parent has tag %s\n", tag.ToNewCString()); // leak!
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
if (PR_FALSE==tag.Equals(aPropName))
|
|
{
|
|
printf("* this is not the style node\n");
|
|
PRInt32 offsetInParent;
|
|
result = nsIEditorSupport::GetChildOffset(newMiddleNode, parent, offsetInParent);
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
if (0!=offsetInParent) {
|
|
printf("* splitting parent %p at offset %d\n", parent, offsetInParent);
|
|
result = nsEditor::SplitNode(parent, offsetInParent, getter_AddRefs(newLeftNode));
|
|
printf("* split created left node %p sibling of parent\n", newLeftNode.get());
|
|
if (gNoisy) {DebugDumpContent(); } // DEBUG
|
|
}
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
nsCOMPtr<nsIDOMNodeList>childNodes;
|
|
result = parent->GetChildNodes(getter_AddRefs(childNodes));
|
|
if (NS_SUCCEEDED(result) && childNodes)
|
|
{
|
|
childNodes->GetLength(&count);
|
|
NS_ASSERTION(count>0, "bad child count in newly split node");
|
|
if ((PRInt32)count!=1)
|
|
{
|
|
printf("* splitting parent %p at offset %d\n", parent, 1);
|
|
result = nsEditor::SplitNode(parent, 1, getter_AddRefs(newMiddleNode));
|
|
printf("* split created middle node %p sibling of parent\n", newMiddleNode.get());
|
|
if (gNoisy) {DebugDumpContent(); } // DEBUG
|
|
}
|
|
else {
|
|
printf("* no need to split parent, newMiddleNode=parent\n");
|
|
newMiddleNode = do_QueryInterface(parent);
|
|
}
|
|
NS_ASSERTION(newMiddleNode, "no middle node created");
|
|
parent->GetParentNode(getter_AddRefs(temp));
|
|
parent = do_QueryInterface(temp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// else we've found the style tag (referred to by "parent")
|
|
// nwMiddleNode is the node that is an ancestor to the selection
|
|
else
|
|
{
|
|
printf("* this is the style node\n");
|
|
PRInt32 offsetInParent;
|
|
result = nsIEditorSupport::GetChildOffset(newMiddleNode, parent, offsetInParent);
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
nsCOMPtr<nsIDOMNodeList>childNodes;
|
|
result = parent->GetChildNodes(getter_AddRefs(childNodes));
|
|
if (NS_SUCCEEDED(result) && childNodes)
|
|
{
|
|
childNodes->GetLength(&count);
|
|
if (0!=offsetInParent && ((PRInt32)count!=offsetInParent+1)) {
|
|
printf("* splitting parent %p at offset %d\n", parent, offsetInParent);
|
|
result = nsEditor::SplitNode(parent, offsetInParent, getter_AddRefs(newLeftNode));
|
|
printf("* split created left node %p sibling of parent\n", newLeftNode.get());
|
|
if (gNoisy) {DebugDumpContent(); } // DEBUG
|
|
}
|
|
if (NS_SUCCEEDED(result))
|
|
{ // promote the selection to the grandparent
|
|
nsCOMPtr<nsIDOMNode>grandParent;
|
|
result = parent->GetParentNode(getter_AddRefs(grandParent));
|
|
if (NS_SUCCEEDED(result) && grandParent)
|
|
{
|
|
printf("* deleting middle node %p\n", newMiddleNode.get());
|
|
result = nsEditor::DeleteNode(newMiddleNode);
|
|
if (gNoisy) {DebugDumpContent(); } // DEBUG
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
PRInt32 position;
|
|
result = nsIEditorSupport::GetChildOffset(parent, grandParent, position);
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
printf("* inserting node %p in grandparent %p at offset %d\n",
|
|
newMiddleNode.get(), grandParent.get(), position);
|
|
result = nsEditor::InsertNode(newMiddleNode, grandParent, position);
|
|
if (gNoisy) {DebugDumpContent(); } // DEBUG
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
PRBool hasChildren=PR_TRUE;
|
|
parent->HasChildNodes(&hasChildren);
|
|
if (PR_FALSE==hasChildren) {
|
|
printf("* deleting empty style node %p\n", parent.get());
|
|
result = nsEditor::DeleteNode(parent);
|
|
if (gNoisy) {DebugDumpContent(); } // DEBUG
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* this should only get called if the only intervening nodes are inline style nodes */
|
|
NS_IMETHODIMP
|
|
nsTextEditor::RemoveTextPropertiesForNodesWithSameParent(nsIDOMNode *aStartNode,
|
|
PRInt32 aStartOffset,
|
|
nsIDOMNode *aEndNode,
|
|
PRInt32 aEndOffset,
|
|
nsIDOMNode *aParent,
|
|
nsIAtom *aPropName)
|
|
{
|
|
printf("not yet implemented\n");
|
|
return NS_OK;
|
|
nsresult result=NS_OK;
|
|
PRBool textPropertySet;
|
|
IsTextPropertySetByContent(aStartNode, aPropName, textPropertySet);
|
|
if (PR_FALSE==textPropertySet)
|
|
{
|
|
nsCOMPtr<nsIDOMNode>newLeftTextNode; // this will be the middle text node
|
|
if (0!=aStartOffset) {
|
|
result = nsEditor::SplitNode(aStartNode, aStartOffset, getter_AddRefs(newLeftTextNode));
|
|
}
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
nsCOMPtr<nsIDOMCharacterData>endNodeAsChar;
|
|
endNodeAsChar = do_QueryInterface(aEndNode);
|
|
if (!endNodeAsChar)
|
|
return NS_ERROR_FAILURE;
|
|
PRUint32 count;
|
|
endNodeAsChar->GetLength(&count);
|
|
nsCOMPtr<nsIDOMNode>newRightTextNode; // this will be the middle text node
|
|
if ((PRInt32)count!=aEndOffset) {
|
|
result = nsEditor::SplitNode(aEndNode, aEndOffset, getter_AddRefs(newRightTextNode));
|
|
}
|
|
else {
|
|
newRightTextNode = do_QueryInterface(aEndNode);
|
|
}
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
PRInt32 offsetInParent;
|
|
if (newLeftTextNode) {
|
|
result = nsIEditorSupport::GetChildOffset(newLeftTextNode, aParent, offsetInParent);
|
|
}
|
|
else {
|
|
offsetInParent = -1; // relies on +1 below in call to CreateNode
|
|
}
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
nsAutoString tag;
|
|
aPropName->ToString(tag);
|
|
// create the new style node, which will be the new parent for the selected nodes
|
|
nsCOMPtr<nsIDOMNode>newStyleNode;
|
|
result = nsEditor::CreateNode(tag, aParent, offsetInParent+1, getter_AddRefs(newStyleNode));
|
|
if (NS_SUCCEEDED(result))
|
|
{ // move the right half of the start node into the new style node
|
|
nsCOMPtr<nsIDOMNode>intermediateNode;
|
|
result = aStartNode->GetNextSibling(getter_AddRefs(intermediateNode));
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
result = nsEditor::DeleteNode(aStartNode);
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
PRInt32 childIndex=0;
|
|
result = nsEditor::InsertNode(aStartNode, newStyleNode, childIndex);
|
|
childIndex++;
|
|
if (NS_SUCCEEDED(result))
|
|
{ // move all the intermediate nodes into the new style node
|
|
nsCOMPtr<nsIDOMNode>nextSibling;
|
|
while (intermediateNode.get() != aEndNode)
|
|
{
|
|
if (!intermediateNode)
|
|
result = NS_ERROR_NULL_POINTER;
|
|
if (NS_FAILED(result)) {
|
|
break;
|
|
}
|
|
// get the next sibling before moving the current child!!!
|
|
intermediateNode->GetNextSibling(getter_AddRefs(nextSibling));
|
|
result = nsEditor::DeleteNode(intermediateNode);
|
|
if (NS_SUCCEEDED(result)) {
|
|
result = nsEditor::InsertNode(intermediateNode, newStyleNode, childIndex);
|
|
childIndex++;
|
|
}
|
|
intermediateNode = do_QueryInterface(nextSibling);
|
|
}
|
|
if (NS_SUCCEEDED(result))
|
|
{ // move the left half of the end node into the new style node
|
|
result = nsEditor::DeleteNode(newRightTextNode);
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
result = nsEditor::InsertNode(newRightTextNode, newStyleNode, childIndex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTextEditor::RemoveTextPropertiesForNodeWithDifferentParents(nsIDOMRange *aRange,
|
|
nsIDOMNode *aStartNode,
|
|
PRInt32 aStartOffset,
|
|
nsIDOMNode *aEndNode,
|
|
PRInt32 aEndOffset,
|
|
nsIDOMNode *aParent,
|
|
nsIAtom *aPropName)
|
|
{
|
|
printf("not yet implemented\n");
|
|
return NS_OK;
|
|
nsresult result=NS_OK;
|
|
if (!aRange || !aStartNode || !aEndNode || !aParent || !aPropName)
|
|
return NS_ERROR_NULL_POINTER;
|
|
// create a style node for the text in the start parent
|
|
nsCOMPtr<nsIDOMNode>parent;
|
|
result = aStartNode->GetParentNode(getter_AddRefs(parent));
|
|
if (NS_FAILED(result)) {
|
|
return result;
|
|
}
|
|
|
|
// create style nodes for all the content between the start and end nodes
|
|
nsCOMPtr<nsIContentIterator>iter;
|
|
result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull,
|
|
kIContentIteratorIID, getter_AddRefs(iter));
|
|
if ((NS_SUCCEEDED(result)) && iter)
|
|
{
|
|
nsCOMPtr<nsIContent>startContent;
|
|
startContent = do_QueryInterface(aStartNode);
|
|
nsCOMPtr<nsIContent>endContent;
|
|
endContent = do_QueryInterface(aEndNode);
|
|
if (startContent && endContent)
|
|
{
|
|
iter->Init(aRange);
|
|
nsCOMPtr<nsIContent> content;
|
|
iter->CurrentNode(getter_AddRefs(content));
|
|
nsAutoString tag;
|
|
aPropName->ToString(tag);
|
|
while (NS_COMFALSE == iter->IsDone())
|
|
{
|
|
if ((content.get() != startContent.get()) &&
|
|
(content.get() != endContent.get()))
|
|
{
|
|
nsCOMPtr<nsIDOMCharacterData>charNode;
|
|
charNode = do_QueryInterface(content);
|
|
if (charNode)
|
|
{
|
|
// only want to wrap the text node in a new style node if it doesn't already have that style
|
|
nsCOMPtr<nsIDOMNode>node;
|
|
node = do_QueryInterface(content);
|
|
PRBool textPropertySet;
|
|
IsTextPropertySetByContent(node, aPropName, textPropertySet);
|
|
if (PR_FALSE==textPropertySet)
|
|
{
|
|
nsCOMPtr<nsIDOMNode>parent;
|
|
charNode->GetParentNode(getter_AddRefs(parent));
|
|
if (!parent) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
nsCOMPtr<nsIContent>parentContent;
|
|
parentContent = do_QueryInterface(parent);
|
|
|
|
PRInt32 offsetInParent;
|
|
parentContent->IndexOf(content, offsetInParent);
|
|
|
|
nsCOMPtr<nsIDOMNode>newStyleNode;
|
|
result = nsEditor::CreateNode(tag, parent, offsetInParent, getter_AddRefs(newStyleNode));
|
|
if (NS_SUCCEEDED(result) && newStyleNode) {
|
|
nsCOMPtr<nsIDOMNode>contentNode;
|
|
contentNode = do_QueryInterface(content);
|
|
result = nsEditor::DeleteNode(contentNode);
|
|
if (NS_SUCCEEDED(result)) {
|
|
result = nsEditor::InsertNode(contentNode, newStyleNode, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// note we don't check the result, we just rely on iter->IsDone
|
|
iter->Next();
|
|
result = iter->CurrentNode(getter_AddRefs(content));
|
|
}
|
|
}
|
|
}
|
|
|
|
nsCOMPtr<nsIDOMCharacterData>nodeAsChar;
|
|
nodeAsChar = do_QueryInterface(aStartNode);
|
|
if (!nodeAsChar)
|
|
return NS_ERROR_FAILURE;
|
|
PRUint32 count;
|
|
nodeAsChar->GetLength(&count);
|
|
result = SetTextPropertiesForNode(aStartNode, parent, aStartOffset, count, aPropName);
|
|
|
|
// create a style node for the text in the end parent
|
|
result = aEndNode->GetParentNode(getter_AddRefs(parent));
|
|
if (NS_FAILED(result)) {
|
|
return result;
|
|
}
|
|
nodeAsChar = do_QueryInterface(aEndNode);
|
|
if (!nodeAsChar)
|
|
return NS_ERROR_FAILURE;
|
|
nodeAsChar->GetLength(&count);
|
|
result = SetTextPropertiesForNode(aEndNode, parent, 0, aEndOffset, aPropName);
|
|
|
|
return result;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTextEditor::SetTypeInStateForProperty(TypeInState &aTypeInState, nsIAtom *aPropName)
|
|
{
|
|
if (!aPropName) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
if (nsIEditProperty::b==aPropName)
|
|
{
|
|
if (PR_TRUE==aTypeInState.IsSet(NS_TYPEINSTATE_BOLD))
|
|
{ // toggle currently set boldness
|
|
aTypeInState.UnSet(NS_TYPEINSTATE_BOLD);
|
|
}
|
|
else
|
|
{ // get the current style and set boldness to the opposite of the current state
|
|
PRBool any = PR_FALSE;
|
|
PRBool all = PR_FALSE;
|
|
PRBool first = PR_FALSE;
|
|
GetTextProperty(aPropName, first, any, all); // operates on current selection
|
|
aTypeInState.SetBold(!any);
|
|
}
|
|
}
|
|
else if (nsIEditProperty::i==aPropName)
|
|
{
|
|
if (PR_TRUE==aTypeInState.IsSet(NS_TYPEINSTATE_ITALIC))
|
|
{ // toggle currently set italicness
|
|
aTypeInState.UnSet(NS_TYPEINSTATE_ITALIC);
|
|
}
|
|
else
|
|
{ // get the current style and set boldness to the opposite of the current state
|
|
PRBool any = PR_FALSE;
|
|
PRBool all = PR_FALSE;
|
|
PRBool first = PR_FALSE;
|
|
GetTextProperty(aPropName, first, any, all); // operates on current selection
|
|
aTypeInState.SetItalic(!any);
|
|
}
|
|
}
|
|
else if (nsIEditProperty::u==aPropName)
|
|
{
|
|
if (PR_TRUE==aTypeInState.IsSet(NS_TYPEINSTATE_UNDERLINE))
|
|
{ // toggle currently set italicness
|
|
aTypeInState.UnSet(NS_TYPEINSTATE_UNDERLINE);
|
|
}
|
|
else
|
|
{ // get the current style and set boldness to the opposite of the current state
|
|
PRBool any = PR_FALSE;
|
|
PRBool all = PR_FALSE;
|
|
PRBool first = PR_FALSE;
|
|
GetTextProperty(aPropName, first, any, all); // operates on current selection
|
|
aTypeInState.SetUnderline(!any);
|
|
}
|
|
}
|
|
else {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
return NS_OK;
|
|
}
|