/* -*- 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 "nsHTMLEditor.h" #include "nsHTMLEditRules.h" #include "nsEditorEventListeners.h" #include "nsInsertHTMLTxn.h" #include "nsIDOMNodeList.h" #include "nsIDOMNSRange.h" #include "nsIDOMDocument.h" #include "nsIDOMEventReceiver.h" #include "nsIDOMKeyListener.h" #include "nsIDOMMouseListener.h" #include "nsIDOMSelection.h" #include "nsIDOMHTMLAnchorElement.h" #include "nsIDOMHTMLImageElement.h" #include "nsIEnumerator.h" #include "nsIContent.h" #include "nsIContentIterator.h" #include "nsEditorCID.h" #include "nsLayoutCID.h" #include "nsIDOMRange.h" #include "nsISupportsArray.h" #include "nsVoidArray.h" #include "nsFileSpec.h" #include "nsIComponentManager.h" #include "nsIServiceManager.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(kInsertHTMLTxnIID, NS_INSERT_HTML_TXN_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(kIHTMLEditorIID, NS_IHTMLEDITOR_IID); static NS_DEFINE_CID(kHTMLEditorCID, NS_HTMLEDITOR_CID); static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID); static NS_DEFINE_IID(kIContentIteratorIID, NS_ICONTENTITERTOR_IID); #ifdef NS_DEBUG static PRBool gNoisy = PR_FALSE; #else static const PRBool gNoisy = PR_FALSE; #endif nsHTMLEditor::nsHTMLEditor() { // Done in nsEditor // NS_INIT_REFCNT(); } nsHTMLEditor::~nsHTMLEditor() { //the autopointers will clear themselves up. } // Adds appropriate AddRef, Release, and QueryInterface methods for derived class //NS_IMPL_ISUPPORTS_INHERITED(nsHTMLEditor, nsTextEditor, nsIHTMLEditor) //NS_IMPL_ADDREF_INHERITED(Class, Super) NS_IMETHODIMP_(nsrefcnt) nsHTMLEditor::AddRef(void) { return nsTextEditor::AddRef(); } //NS_IMPL_RELEASE_INHERITED(Class, Super) NS_IMETHODIMP_(nsrefcnt) nsHTMLEditor::Release(void) { return nsTextEditor::Release(); } //NS_IMPL_QUERY_INTERFACE_INHERITED(Class, Super, AdditionalInterface) NS_IMETHODIMP nsHTMLEditor::QueryInterface(REFNSIID aIID, void** aInstancePtr) { if (!aInstancePtr) return NS_ERROR_NULL_POINTER; if (aIID.Equals(nsIHTMLEditor::GetIID())) { *aInstancePtr = NS_STATIC_CAST(nsIHTMLEditor*, this); NS_ADDREF_THIS(); return NS_OK; } return nsTextEditor::QueryInterface(aIID, aInstancePtr); } NS_IMETHODIMP nsHTMLEditor::Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell) { NS_PRECONDITION(nsnull!=aDoc && nsnull!=aPresShell, "bad arg"); nsresult result=NS_ERROR_NULL_POINTER; if ((nsnull!=aDoc) && (nsnull!=aPresShell)) { return nsTextEditor::Init(aDoc, aPresShell); } return result; } void nsHTMLEditor::InitRules() { // instantiate the rules for this text editor // XXX: we should be told which set of rules to instantiate mRules = new nsHTMLEditRules(); mRules->Init(this); } NS_IMETHODIMP nsHTMLEditor::SetTextProperty(nsIAtom *aProperty, const nsString *aAttribute, const nsString *aValue) { return nsTextEditor::SetTextProperty(aProperty, aAttribute, aValue); } NS_IMETHODIMP nsHTMLEditor::GetTextProperty(nsIAtom *aProperty, const nsString *aAttribute, const nsString *aValue, PRBool &aFirst, PRBool &aAny, PRBool &aAll) { return nsTextEditor::GetTextProperty(aProperty, aAttribute, aValue, aFirst, aAny, aAll); } NS_IMETHODIMP nsHTMLEditor::RemoveTextProperty(nsIAtom *aProperty, const nsString *aAttribute) { return nsTextEditor::RemoveTextProperty(aProperty, aAttribute); } NS_IMETHODIMP nsHTMLEditor::DeleteSelection(nsIEditor::ECollapsedSelectionAction aAction) { return nsTextEditor::DeleteSelection(aAction); } NS_IMETHODIMP nsHTMLEditor::InsertText(const nsString& aStringToInsert) { return nsTextEditor::InsertText(aStringToInsert); } NS_IMETHODIMP nsHTMLEditor::InsertBreak() { nsresult result; if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } nsCOMPtr selection; PRBool cancel= PR_FALSE; result = nsEditor::BeginTransaction(); if (NS_FAILED(result)) { return result; } // pre-process nsEditor::GetSelection(getter_AddRefs(selection)); nsTextRulesInfo ruleInfo(nsHTMLEditRules::kInsertBreak); result = mRules->WillDoAction(selection, &ruleInfo, &cancel); if ((PR_FALSE==cancel) && (NS_SUCCEEDED(result))) { // create the new BR node nsCOMPtr newNode; nsAutoString tag("BR"); result = nsEditor::DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode)); if (NS_SUCCEEDED(result) && newNode) { // set the selection to the new node nsCOMPtrparent; result = newNode->GetParentNode(getter_AddRefs(parent)); if (NS_SUCCEEDED(result) && parent) { PRInt32 offsetInParent=-1; // we use the -1 as a marker to see if we need to compute this or not nsCOMPtrnextNode; newNode->GetNextSibling(getter_AddRefs(nextNode)); if (nextNode) { nsCOMPtrnextTextNode; nextTextNode = do_QueryInterface(nextNode); if (!nextTextNode) { nextNode = do_QueryInterface(newNode); } else { offsetInParent=0; } } else { nextNode = do_QueryInterface(newNode); } result = nsEditor::GetSelection(getter_AddRefs(selection)); if (NS_SUCCEEDED(result)) { if (-1==offsetInParent) { nextNode->GetParentNode(getter_AddRefs(parent)); result = GetChildOffset(nextNode, parent, offsetInParent); if (NS_SUCCEEDED(result)) { selection->Collapse(parent, offsetInParent+1); // +1 to insert just after the break } } else { selection->Collapse(nextNode, offsetInParent); } } } } // post-process, always called if WillInsertBreak didn't return cancel==PR_TRUE result = mRules->DidDoAction(selection, &ruleInfo, 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 nsHTMLEditor::GetParagraphFormat(nsString& aParagraphFormat) { nsresult result = NS_ERROR_NOT_INITIALIZED; return result; } NS_IMETHODIMP nsHTMLEditor::SetParagraphFormat(const nsString& aParagraphFormat) { nsresult result = NS_ERROR_NOT_INITIALIZED; //Kinda sad to waste memory just to force lower case nsAutoString tag = aParagraphFormat; tag.ToLowerCase(); if (tag == "normal" || tag == "p") { result = RemoveParagraphStyle(); } else { result = ReplaceBlockParent(tag); } // 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; } // Methods shared with the base editor. // Note: We could call each of these via nsTextEditor -- is that better? NS_IMETHODIMP nsHTMLEditor::EnableUndo(PRBool aEnable) { return nsTextEditor::EnableUndo(aEnable); } NS_IMETHODIMP nsHTMLEditor::Undo(PRUint32 aCount) { return nsTextEditor::Undo(aCount); } NS_IMETHODIMP nsHTMLEditor::CanUndo(PRBool &aIsEnabled, PRBool &aCanUndo) { return nsTextEditor::CanUndo(aIsEnabled, aCanUndo); } NS_IMETHODIMP nsHTMLEditor::Redo(PRUint32 aCount) { return nsTextEditor::Redo(aCount); } NS_IMETHODIMP nsHTMLEditor::CanRedo(PRBool &aIsEnabled, PRBool &aCanRedo) { return nsTextEditor::CanRedo(aIsEnabled, aCanRedo); } NS_IMETHODIMP nsHTMLEditor::BeginTransaction() { return nsTextEditor::BeginTransaction(); } NS_IMETHODIMP nsHTMLEditor::EndTransaction() { return nsTextEditor::EndTransaction(); } NS_IMETHODIMP nsHTMLEditor::MoveSelectionUp(nsIAtom *aIncrement, PRBool aExtendSelection) { return nsTextEditor::MoveSelectionUp(aIncrement, aExtendSelection); } NS_IMETHODIMP nsHTMLEditor::MoveSelectionDown(nsIAtom *aIncrement, PRBool aExtendSelection) { return nsTextEditor::MoveSelectionDown(aIncrement, aExtendSelection); } NS_IMETHODIMP nsHTMLEditor::MoveSelectionNext(nsIAtom *aIncrement, PRBool aExtendSelection) { return nsTextEditor::MoveSelectionNext(aIncrement, aExtendSelection); } NS_IMETHODIMP nsHTMLEditor::MoveSelectionPrevious(nsIAtom *aIncrement, PRBool aExtendSelection) { return nsTextEditor::MoveSelectionPrevious(aIncrement, aExtendSelection); } NS_IMETHODIMP nsHTMLEditor::SelectNext(nsIAtom *aIncrement, PRBool aExtendSelection) { return nsTextEditor::SelectNext(aIncrement, aExtendSelection); } NS_IMETHODIMP nsHTMLEditor::SelectPrevious(nsIAtom *aIncrement, PRBool aExtendSelection) { return nsTextEditor::SelectPrevious(aIncrement, aExtendSelection); } NS_IMETHODIMP nsHTMLEditor::SelectAll() { return nsTextEditor::SelectAll(); } NS_IMETHODIMP nsHTMLEditor::ScrollUp(nsIAtom *aIncrement) { return nsTextEditor::ScrollUp(aIncrement); } NS_IMETHODIMP nsHTMLEditor::ScrollDown(nsIAtom *aIncrement) { return nsTextEditor::ScrollDown(aIncrement); } NS_IMETHODIMP nsHTMLEditor::ScrollIntoView(PRBool aScrollToBegin) { return nsTextEditor::ScrollIntoView(aScrollToBegin); } NS_IMETHODIMP nsHTMLEditor::Save() { return nsTextEditor::Save(); } NS_IMETHODIMP nsHTMLEditor::SaveAs(PRBool aSavingCopy) { return nsTextEditor::SaveAs(aSavingCopy); } NS_IMETHODIMP nsHTMLEditor::Cut() { return nsTextEditor::Cut(); } NS_IMETHODIMP nsHTMLEditor::Copy() { return nsTextEditor::Copy(); } NS_IMETHODIMP nsHTMLEditor::Paste() { return nsTextEditor::Paste(); } NS_IMETHODIMP nsHTMLEditor::Insert(nsString& aInputString) { nsresult res; nsEditor::BeginTransaction(); nsEditor::DeleteSelection(nsIEditor::eDoNothing); // Make the transaction for insert html: nsInsertHTMLTxn* insertHTMLTxn = 0; res = TransactionFactory::GetNewTransaction(kInsertHTMLTxnIID, (EditTxn **)&insertHTMLTxn); if (NS_SUCCEEDED(res)) { res = insertHTMLTxn->Init(aInputString, this); if (NS_SUCCEEDED(res)) res = Do(insertHTMLTxn); // XXX How is it that we don't have to release the transaction? } nsEditor::EndTransaction(); if (NS_FAILED(res)) printf("Couldn't insert html: error was %d\n", res); return res; } NS_IMETHODIMP nsHTMLEditor::OutputText(nsString& aOutputString) { return nsTextEditor::OutputText(aOutputString); } NS_IMETHODIMP nsHTMLEditor::OutputHTML(nsString& aOutputString) { return nsTextEditor::OutputHTML(aOutputString); } NS_IMETHODIMP nsHTMLEditor::OutputText(nsIOutputStream* aOutputStream, nsString* aCharsetOverride) { return nsTextEditor::OutputText(aOutputStream,aCharsetOverride); } NS_IMETHODIMP nsHTMLEditor::OutputHTML(nsIOutputStream* aOutputStream,nsString* aCharsetOverride) { return nsTextEditor::OutputHTML(aOutputStream,aCharsetOverride); } NS_IMETHODIMP nsHTMLEditor::CopyAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode) { return nsTextEditor::CopyAttributes(aDestNode, aSourceNode); } //================================================================ // HTML Editor methods // // Note: Table Editing methods are implemented in EditTable.cpp // // get the paragraph style(s) for the selection NS_IMETHODIMP nsHTMLEditor::GetParagraphStyle(nsStringArray *aTagList) { if (gNoisy) { printf("---------- nsHTMLEditor::GetParagraphStyle ----------\n"); } if (!aTagList) { return NS_ERROR_NULL_POINTER; } nsresult result; nsCOMPtrselection; result = nsEditor::GetSelection(getter_AddRefs(selection)); if ((NS_SUCCEEDED(result)) && selection) { nsCOMPtr enumerator; enumerator = do_QueryInterface(selection); if (enumerator) { enumerator->First(); nsISupports *currentItem; result = enumerator->CurrentItem(¤tItem); if ((NS_SUCCEEDED(result)) && (nsnull!=currentItem)) { nsCOMPtr range( do_QueryInterface(currentItem) ); // scan the range for all the independent block content blockSections // and get the block parent of each nsISupportsArray *blockSections; result = NS_NewISupportsArray(&blockSections); if ((NS_SUCCEEDED(result)) && blockSections) { result = GetBlockSectionsForRange(range, blockSections); if (NS_SUCCEEDED(result)) { nsIDOMRange *subRange; subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); while (subRange && (NS_SUCCEEDED(result))) { nsCOMPtrstartParent; result = subRange->GetStartParent(getter_AddRefs(startParent)); if (NS_SUCCEEDED(result) && startParent) { nsCOMPtr blockParent; result = GetBlockParent(startParent, getter_AddRefs(blockParent)); if (NS_SUCCEEDED(result) && blockParent) { nsAutoString blockParentTag; blockParent->GetTagName(blockParentTag); PRBool isRoot; IsRootTag(blockParentTag, isRoot); if ((PR_FALSE==isRoot) && (-1==aTagList->IndexOf(blockParentTag))) { aTagList->AppendString(blockParentTag); } } } NS_RELEASE(subRange); blockSections->RemoveElementAt(0); subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); } } NS_RELEASE(blockSections); } } } } return result; } // use this when block parents are to be added regardless of current state NS_IMETHODIMP nsHTMLEditor::AddBlockParent(nsString& aParentTag) { if (gNoisy) { char *tag = aParentTag.ToNewCString(); printf("---------- nsHTMLEditor::AddBlockParent %s ----------\n", tag); delete [] tag; } nsresult result=NS_ERROR_NOT_INITIALIZED; nsCOMPtrselection; result = nsEditor::GetSelection(getter_AddRefs(selection)); if ((NS_SUCCEEDED(result)) && selection) { // set the block parent for all selected ranges nsEditor::BeginTransaction(); nsCOMPtr enumerator; enumerator = do_QueryInterface(selection); if (enumerator) { enumerator->First(); nsISupports *currentItem; result = enumerator->CurrentItem(¤tItem); if ((NS_SUCCEEDED(result)) && (nsnull!=currentItem)) { nsCOMPtr range( do_QueryInterface(currentItem) ); // scan the range for all the independent block content blockSections // and apply the transformation to them result = ReParentContentOfRange(range, aParentTag, eInsertParent); } } nsEditor::EndTransaction(); if (NS_SUCCEEDED(result)) { // set the selection // XXX: can't do anything until I can create ranges } } if (gNoisy) {printf("Finished nsHTMLEditor::AddBlockParent with this content:\n"); DebugDumpContent(); } // DEBUG return result; } // use this when a paragraph type is being transformed from one type to another NS_IMETHODIMP nsHTMLEditor::ReplaceBlockParent(nsString& aParentTag) { if (gNoisy) { char *tag = aParentTag.ToNewCString(); printf("---------- nsHTMLEditor::ReplaceBlockParent %s ----------\n", tag); delete [] tag; } nsresult result=NS_ERROR_NOT_INITIALIZED; nsCOMPtrselection; result = nsEditor::GetSelection(getter_AddRefs(selection)); if ((NS_SUCCEEDED(result)) && selection) { // set the block parent for all selected ranges nsEditor::BeginTransaction(); nsCOMPtr enumerator; enumerator = do_QueryInterface(selection); if (enumerator) { enumerator->First(); nsISupports *currentItem; result = enumerator->CurrentItem(¤tItem); if ((NS_SUCCEEDED(result)) && (nsnull!=currentItem)) { nsCOMPtr range( do_QueryInterface(currentItem) ); // scan the range for all the independent block content blockSections // and apply the transformation to them result = ReParentContentOfRange(range, aParentTag, eReplaceParent); } } nsEditor::EndTransaction(); } if (gNoisy) {printf("Finished nsHTMLEditor::AddBlockParent with this content:\n"); DebugDumpContent(); } // DEBUG return result; } NS_IMETHODIMP nsHTMLEditor::ReParentContentOfNode(nsIDOMNode *aNode, nsString &aParentTag, BlockTransformationType aTransformation) { if (!aNode) { return NS_ERROR_NULL_POINTER; } if (gNoisy) { char *tag = aParentTag.ToNewCString(); printf("---------- ReParentContentOfNode(%p,%s,%d) -----------\n", aNode, tag, aTransformation); delete [] tag; } // find the current block parent, or just use aNode if it is a block node nsCOMPtrblockParentElement; nsCOMPtrnodeToReParent; // this is the node we'll operate on, by default it's aNode nsresult result = aNode->QueryInterface(nsIDOMNode::GetIID(), getter_AddRefs(nodeToReParent)); PRBool nodeIsInline; PRBool nodeIsBlock=PR_FALSE; nsTextEditor::IsNodeInline(aNode, nodeIsInline); if (PR_FALSE==nodeIsInline) { nsresult QIResult; nsCOMPtrnodeAsText; QIResult = aNode->QueryInterface(nsIDOMCharacterData::GetIID(), getter_AddRefs(nodeAsText)); if (NS_FAILED(QIResult) || !nodeAsText) { nodeIsBlock=PR_TRUE; } } // if aNode is the block parent, then the node to reparent is one of its children if (PR_TRUE==nodeIsBlock) { result = aNode->QueryInterface(nsIDOMNode::GetIID(), getter_AddRefs(blockParentElement)); if (NS_SUCCEEDED(result) && blockParentElement) { result = aNode->GetFirstChild(getter_AddRefs(nodeToReParent)); } } else { // we just need to get the block parent of aNode result = nsTextEditor::GetBlockParent(aNode, getter_AddRefs(blockParentElement)); } // at this point, we must have a good result, a node to reparent, and a block parent if (!nodeToReParent) { return NS_ERROR_UNEXPECTED;} if (!blockParentElement) { return NS_ERROR_NULL_POINTER;} if (NS_SUCCEEDED(result)) { nsCOMPtr newParentNode; nsCOMPtr blockParentNode = do_QueryInterface(blockParentElement); // we need to treat nodes directly inside the body differently nsAutoString parentTag; blockParentElement->GetTagName(parentTag); PRBool isRoot; IsRootTag(parentTag, isRoot); if (PR_TRUE==isRoot) { // if nodeToReParent is a text node, we have Text. // re-parent Text into a new at the offset of Text in // so we end up with Text // ignore aTransformation, replaces act like inserts nsCOMPtr nodeAsText = do_QueryInterface(nodeToReParent); if (nodeAsText) { result = ReParentBlockContent(nodeToReParent, aParentTag, blockParentNode, parentTag, aTransformation, getter_AddRefs(newParentNode)); } else { // this is the case of an insertion point between 2 non-text objects // XXX: how to you know it's an insertion point??? PRInt32 offsetInParent=0; result = GetChildOffset(nodeToReParent, blockParentNode, offsetInParent); NS_ASSERTION((NS_SUCCEEDED(result)), "bad result from GetChildOffset"); // otherwise, just create the block parent at the selection result = nsTextEditor::CreateNode(aParentTag, blockParentNode, offsetInParent, getter_AddRefs(newParentNode)); // XXX: need to move some of the children of blockParentNode into the newParentNode? // XXX: need to create a bogus text node inside this new block? // that means, I need to generalize bogus node handling } } else { // the block parent is not a ROOT, // for the selected block content, transform blockParentNode if (((eReplaceParent==aTransformation) && (PR_FALSE==parentTag.EqualsIgnoreCase(aParentTag))) || (eInsertParent==aTransformation)) { if (gNoisy) { DebugDumpContent(); } // DEBUG result = ReParentBlockContent(nodeToReParent, aParentTag, blockParentNode, parentTag, aTransformation, getter_AddRefs(newParentNode)); if ((NS_SUCCEEDED(result)) && (newParentNode) && (eReplaceParent==aTransformation)) { PRBool hasChildren; blockParentNode->HasChildNodes(&hasChildren); if (PR_FALSE==hasChildren) { result = nsEditor::DeleteNode(blockParentNode); if (gNoisy) { printf("deleted old block parent node %p\n", blockParentNode.get()); DebugDumpContent(); // DEBUG } } } } else { // otherwise, it's a no-op if (gNoisy) { printf("AddBlockParent is a no-op for this collapsed selection.\n"); } } } } return result; } NS_IMETHODIMP nsHTMLEditor::ReParentBlockContent(nsIDOMNode *aNode, nsString &aParentTag, nsIDOMNode *aBlockParentNode, nsString &aBlockParentTag, BlockTransformationType aTransformation, nsIDOMNode **aNewParentNode) { if (!aNode || !aBlockParentNode || !aNewParentNode) { return NS_ERROR_NULL_POINTER; } nsCOMPtr blockParentNode = do_QueryInterface(aBlockParentNode); PRBool removeBlockParent = PR_FALSE; PRBool removeBreakBefore = PR_FALSE; PRBool removeBreakAfter = PR_FALSE; nsCOMPtrancestor; nsresult result = aNode->GetParentNode(getter_AddRefs(ancestor)); nsCOMPtrpreviousAncestor = do_QueryInterface(aNode); while (NS_SUCCEEDED(result) && ancestor) { nsCOMPtrancestorElement = do_QueryInterface(ancestor); nsAutoString ancestorTag; ancestorElement->GetTagName(ancestorTag); if (ancestorTag.EqualsIgnoreCase(aBlockParentTag)) { break; // previousAncestor will contain the node to operate on } previousAncestor = do_QueryInterface(ancestor); result = ancestorElement->GetParentNode(getter_AddRefs(ancestor)); } // now, previousAncestor is the node we are operating on nsCOMPtrleftNode, rightNode; result = GetBlockSection(previousAncestor, getter_AddRefs(leftNode), getter_AddRefs(rightNode)); if ((NS_SUCCEEDED(result)) && leftNode && rightNode) { // determine some state for managing
s around the new block PRBool isSubordinateBlock = PR_FALSE; // if true, the content is already in a subordinate block PRBool isRootBlock = PR_FALSE; // if true, the content is in a root block nsCOMPtrblockParentElement = do_QueryInterface(blockParentNode); if (blockParentElement) { nsAutoString blockParentTag; blockParentElement->GetTagName(blockParentTag); IsSubordinateBlock(blockParentTag, isSubordinateBlock); IsRootTag(blockParentTag, isRootBlock); } if (PR_TRUE==isRootBlock) { // we're creating a block element where a block element did not previously exist removeBreakBefore = PR_TRUE; removeBreakAfter = PR_TRUE; } // apply the transformation PRInt32 offsetInParent=0; if (eInsertParent==aTransformation || PR_TRUE==isRootBlock) { result = GetChildOffset(leftNode, blockParentNode, offsetInParent); NS_ASSERTION((NS_SUCCEEDED(result)), "bad result from GetChildOffset"); result = nsTextEditor::CreateNode(aParentTag, blockParentNode, offsetInParent, aNewParentNode); if (gNoisy) { printf("created a node in blockParentNode at offset %d\n", offsetInParent); } } else { nsCOMPtr grandParent; result = blockParentNode->GetParentNode(getter_AddRefs(grandParent)); if ((NS_SUCCEEDED(result)) && grandParent) { nsCOMPtrfirstChildNode, lastChildNode; blockParentNode->GetFirstChild(getter_AddRefs(firstChildNode)); blockParentNode->GetLastChild(getter_AddRefs(lastChildNode)); if (firstChildNode==leftNode && lastChildNode==rightNode) { result = GetChildOffset(blockParentNode, grandParent, offsetInParent); NS_ASSERTION((NS_SUCCEEDED(result)), "bad result from GetChildOffset"); result = nsTextEditor::CreateNode(aParentTag, grandParent, offsetInParent, aNewParentNode); if (gNoisy) { printf("created a node in grandParent at offset %d\n", offsetInParent); } } else { // We're in the case where the content of blockParentNode is separated by
's, // creating multiple block content ranges. // Split blockParentNode around the blockContent if (gNoisy) { printf("splitting a node because of
s\n"); } nsCOMPtr newLeftNode; if (firstChildNode!=leftNode) { result = GetChildOffset(leftNode, blockParentNode, offsetInParent); if (gNoisy) { printf("splitting left at %d\n", offsetInParent); } result = SplitNode(blockParentNode, offsetInParent, getter_AddRefs(newLeftNode)); // after this split, blockParentNode still contains leftNode and rightNode } if (lastChildNode!=rightNode) { result = GetChildOffset(rightNode, blockParentNode, offsetInParent); offsetInParent++; if (gNoisy) { printf("splitting right at %d\n", offsetInParent); } result = SplitNode(blockParentNode, offsetInParent, getter_AddRefs(newLeftNode)); blockParentNode = do_QueryInterface(newLeftNode); } result = GetChildOffset(leftNode, blockParentNode, offsetInParent); NS_ASSERTION((NS_SUCCEEDED(result)), "bad result from GetChildOffset"); result = nsTextEditor::CreateNode(aParentTag, blockParentNode, offsetInParent, aNewParentNode); if (gNoisy) { printf("created a node in blockParentNode at offset %d\n", offsetInParent); } // what we need to do here is remove the existing block parent when we're all done. removeBlockParent = PR_TRUE; } } } if ((NS_SUCCEEDED(result)) && *aNewParentNode) { // move all the children/contents of blockParentNode to aNewParentNode nsCOMPtrchildNode = do_QueryInterface(rightNode); nsCOMPtrpreviousSiblingNode; while (NS_SUCCEEDED(result) && childNode) { childNode->GetPreviousSibling(getter_AddRefs(previousSiblingNode)); // explicitly delete of childNode from it's current parent // can't just rely on DOM semantics of InsertNode doing the delete implicitly, doesn't undo! result = nsEditor::DeleteNode(childNode); if (NS_SUCCEEDED(result)) { result = nsEditor::InsertNode(childNode, *aNewParentNode, 0); if (gNoisy) { printf("re-parented sibling node %p\n", childNode.get()); } } if (childNode==leftNode || rightNode==leftNode) { break; } childNode = do_QueryInterface(previousSiblingNode); } // end while loop } // clean up the surrounding content to maintain vertical whitespace if (NS_SUCCEEDED(result)) { // if the prior node is a
and we did something to change vertical whitespacing, delete the
nsCOMPtr brNode; result = GetPriorNode(leftNode, PR_TRUE, getter_AddRefs(brNode)); if (NS_SUCCEEDED(result) && brNode) { nsCOMPtr brContent = do_QueryInterface(brNode); if (brContent) { nsCOMPtr brContentTag; brContent->GetTag(*getter_AddRefs(brContentTag)); if (nsIEditProperty::br==brContentTag.get()) { result = DeleteNode(brNode); } } } // if the next node is a
and we did something to change vertical whitespacing, delete the
if (NS_SUCCEEDED(result)) { result = GetNextNode(rightNode, PR_TRUE, getter_AddRefs(brNode)); if (NS_SUCCEEDED(result) && brNode) { nsCOMPtr brContent = do_QueryInterface(brNode); if (brContent) { nsCOMPtr brContentTag; brContent->GetTag(*getter_AddRefs(brContentTag)); if (nsIEditProperty::br==brContentTag.get()) { result = DeleteNode(brNode); } } } } } if ((NS_SUCCEEDED(result)) && (PR_TRUE==removeBlockParent)) { // we determined we need to remove the previous block parent. Do it! // go through list backwards so deletes don't interfere with the iteration nsCOMPtr childNodes; result = blockParentNode->GetChildNodes(getter_AddRefs(childNodes)); if ((NS_SUCCEEDED(result)) && (childNodes)) { nsCOMPtrgrandParent; blockParentNode->GetParentNode(getter_AddRefs(grandParent)); PRInt32 offsetInParent; result = GetChildOffset(blockParentNode, grandParent, offsetInParent); PRUint32 childCount; childNodes->GetLength(&childCount); PRInt32 i=childCount-1; for ( ; ((NS_SUCCEEDED(result)) && (0<=i)); i--) { nsCOMPtr childNode; result = childNodes->Item(i, getter_AddRefs(childNode)); if ((NS_SUCCEEDED(result)) && (childNode)) { result = nsTextEditor::DeleteNode(childNode); if (NS_SUCCEEDED(result)) { result = nsTextEditor::InsertNode(childNode, grandParent, offsetInParent); } } } if (gNoisy) { printf("removing the old block parent %p\n", blockParentNode.get()); } result = nsTextEditor::DeleteNode(blockParentNode); } } } return result; } NS_IMETHODIMP nsHTMLEditor::ReParentContentOfRange(nsIDOMRange *aRange, nsString &aParentTag, BlockTransformationType aTranformation) { if (!aRange) { return NS_ERROR_NULL_POINTER; } nsresult result; nsISupportsArray *blockSections; result = NS_NewISupportsArray(&blockSections); if ((NS_SUCCEEDED(result)) && blockSections) { result = GetBlockSectionsForRange(aRange, blockSections); if (NS_SUCCEEDED(result)) { nsIDOMRange *subRange; subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); while (subRange && (NS_SUCCEEDED(result))) { nsCOMPtrstartParent; result = subRange->GetStartParent(getter_AddRefs(startParent)); if (NS_SUCCEEDED(result) && startParent) { if (gNoisy) { printf("ReParentContentOfRange calling ReParentContentOfNode\n"); } result = ReParentContentOfNode(startParent, aParentTag, aTranformation); } NS_RELEASE(subRange); blockSections->RemoveElementAt(0); subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); } } NS_RELEASE(blockSections); } return result; } NS_IMETHODIMP nsHTMLEditor::RemoveParagraphStyle() { if (gNoisy) { printf("---------- nsHTMLEditor::RemoveParagraphStyle ----------\n"); } nsresult result=NS_ERROR_NOT_INITIALIZED; nsCOMPtrselection; result = nsEditor::GetSelection(getter_AddRefs(selection)); if ((NS_SUCCEEDED(result)) && selection) { nsEditor::BeginTransaction(); nsCOMPtr enumerator; enumerator = do_QueryInterface(selection); if (enumerator) { enumerator->First(); nsISupports *currentItem; result = enumerator->CurrentItem(¤tItem); if ((NS_SUCCEEDED(result)) && (nsnull!=currentItem)) { nsCOMPtr range( do_QueryInterface(currentItem) ); result = RemoveParagraphStyleFromRange(range); } } nsEditor::EndTransaction(); } return result; } NS_IMETHODIMP nsHTMLEditor::RemoveParagraphStyleFromRange(nsIDOMRange *aRange) { if (!aRange) { return NS_ERROR_NULL_POINTER; } nsresult result; nsISupportsArray *blockSections; result = NS_NewISupportsArray(&blockSections); if ((NS_SUCCEEDED(result)) && blockSections) { result = GetBlockSectionsForRange(aRange, blockSections); if (NS_SUCCEEDED(result)) { nsIDOMRange *subRange; subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); while (subRange && (NS_SUCCEEDED(result))) { result = RemoveParagraphStyleFromBlockContent(subRange); NS_RELEASE(subRange); blockSections->RemoveElementAt(0); subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); } } NS_RELEASE(blockSections); } return result; } NS_IMETHODIMP nsHTMLEditor::RemoveParagraphStyleFromBlockContent(nsIDOMRange *aRange) { if (!aRange) { return NS_ERROR_NULL_POINTER; } nsresult result; nsCOMPtrstartParent; aRange->GetStartParent(getter_AddRefs(startParent)); nsCOMPtrblockParentElement; result = nsTextEditor::GetBlockParent(startParent, getter_AddRefs(blockParentElement)); while ((NS_SUCCEEDED(result)) && blockParentElement) { nsAutoString blockParentTag; blockParentElement->GetTagName(blockParentTag); PRBool isSubordinateBlock; IsSubordinateBlock(blockParentTag, isSubordinateBlock); if (PR_FALSE==isSubordinateBlock) { break; } else { // go through list backwards so deletes don't interfere with the iteration nsCOMPtr childNodes; result = blockParentElement->GetChildNodes(getter_AddRefs(childNodes)); if ((NS_SUCCEEDED(result)) && (childNodes)) { nsCOMPtrgrandParent; blockParentElement->GetParentNode(getter_AddRefs(grandParent)); PRInt32 offsetInParent; result = GetChildOffset(blockParentElement, grandParent, offsetInParent); PRUint32 childCount; childNodes->GetLength(&childCount); PRInt32 i=childCount-1; for ( ; ((NS_SUCCEEDED(result)) && (0<=i)); i--) { nsCOMPtr childNode; result = childNodes->Item(i, getter_AddRefs(childNode)); if ((NS_SUCCEEDED(result)) && (childNode)) { result = nsTextEditor::DeleteNode(childNode); if (NS_SUCCEEDED(result)) { result = nsTextEditor::InsertNode(childNode, grandParent, offsetInParent); } } } if (NS_SUCCEEDED(result)) { result = nsTextEditor::DeleteNode(blockParentElement); } } } result = nsTextEditor::GetBlockParent(startParent, getter_AddRefs(blockParentElement)); } return result; } NS_IMETHODIMP nsHTMLEditor::RemoveParent(const nsString &aParentTag) { if (gNoisy) { printf("---------- nsHTMLEditor::RemoveParent ----------\n"); } nsresult result=NS_ERROR_NOT_INITIALIZED; nsCOMPtrselection; result = nsEditor::GetSelection(getter_AddRefs(selection)); if ((NS_SUCCEEDED(result)) && selection) { nsEditor::BeginTransaction(); nsCOMPtr enumerator; enumerator = do_QueryInterface(selection); if (enumerator) { enumerator->First(); nsISupports *currentItem; result = enumerator->CurrentItem(¤tItem); if ((NS_SUCCEEDED(result)) && (nsnull!=currentItem)) { nsCOMPtr range( do_QueryInterface(currentItem) ); result = RemoveParentFromRange(aParentTag, range); } } nsEditor::EndTransaction(); } return result; } NS_IMETHODIMP nsHTMLEditor::RemoveParentFromRange(const nsString &aParentTag, nsIDOMRange *aRange) { if (!aRange) { return NS_ERROR_NULL_POINTER; } nsresult result; nsISupportsArray *blockSections; result = NS_NewISupportsArray(&blockSections); if ((NS_SUCCEEDED(result)) && blockSections) { result = GetBlockSectionsForRange(aRange, blockSections); if (NS_SUCCEEDED(result)) { nsIDOMRange *subRange; subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); while (subRange && (NS_SUCCEEDED(result))) { result = RemoveParentFromBlockContent(aParentTag, subRange); NS_RELEASE(subRange); blockSections->RemoveElementAt(0); subRange = (nsIDOMRange *)(blockSections->ElementAt(0)); } } NS_RELEASE(blockSections); } return result; } NS_IMETHODIMP nsHTMLEditor::RemoveParentFromBlockContent(const nsString &aParentTag, nsIDOMRange *aRange) { if (!aRange) { return NS_ERROR_NULL_POINTER; } nsresult result; nsCOMPtrstartParent; result = aRange->GetStartParent(getter_AddRefs(startParent)); if ((NS_SUCCEEDED(result)) && startParent) { nsCOMPtrparentNode; nsCOMPtrparentElement; result = startParent->GetParentNode(getter_AddRefs(parentNode)); while ((NS_SUCCEEDED(result)) && parentNode) { parentElement = do_QueryInterface(parentNode); nsAutoString parentTag; parentElement->GetTagName(parentTag); PRBool isRoot; IsRootTag(parentTag, isRoot); if (aParentTag.EqualsIgnoreCase(parentTag)) { // go through list backwards so deletes don't interfere with the iteration nsCOMPtr childNodes; result = parentElement->GetChildNodes(getter_AddRefs(childNodes)); if ((NS_SUCCEEDED(result)) && (childNodes)) { nsCOMPtrgrandParent; parentElement->GetParentNode(getter_AddRefs(grandParent)); PRInt32 offsetInParent; result = GetChildOffset(parentElement, grandParent, offsetInParent); PRUint32 childCount; childNodes->GetLength(&childCount); PRInt32 i=childCount-1; for ( ; ((NS_SUCCEEDED(result)) && (0<=i)); i--) { nsCOMPtr childNode; result = childNodes->Item(i, getter_AddRefs(childNode)); if ((NS_SUCCEEDED(result)) && (childNode)) { result = nsTextEditor::DeleteNode(childNode); if (NS_SUCCEEDED(result)) { result = nsTextEditor::InsertNode(childNode, grandParent, offsetInParent); } } } if (NS_SUCCEEDED(result)) { result = nsTextEditor::DeleteNode(parentElement); } } break; } else if (PR_TRUE==isRoot) { // hit a local root node, terminate loop break; } result = parentElement->GetParentNode(getter_AddRefs(parentNode)); } } return result; } NS_IMETHODIMP nsHTMLEditor::Indent(nsString& aIndent) { nsresult res = nsEditor::BeginTransaction(); if (NS_FAILED(res)) return res; nsCOMPtr node; PRInt32 offset; // Find out if the selection is collapsed: nsCOMPtr selection; res = GetSelection(getter_AddRefs(selection)); if (NS_FAILED(res) || !selection) { // my kingdom for exceptions nsEditor::EndTransaction(); return res; } PRBool isCollapsed; res = selection->GetIsCollapsed(&isCollapsed); if (NS_FAILED(res)) { nsEditor::EndTransaction(); return res; } res = GetStartNodeAndOffset(selection, &node, &offset); if (!node) res = NS_ERROR_FAILURE; if (NS_FAILED(res)) { nsEditor::EndTransaction(); return res; } nsAutoString inward("indent"); if (aIndent == inward) { if (isCollapsed) { // have to find a place to put the blockquote nsCOMPtr parent = node; nsCOMPtr topChild = node; nsCOMPtr tmp; nsAutoString bq("blockquote"); while ( !CanContainTag(parent, bq)) { parent->GetParentNode(getter_AddRefs(tmp)); if (!tmp) { nsEditor::EndTransaction(); return NS_ERROR_FAILURE; } topChild = parent; parent = tmp; } if (parent != node) { // we need to split up to the child of parent res = SplitNodeDeep(topChild, node, offset); if (NS_FAILED(res)) { nsEditor::EndTransaction(); return res; } // topChild already went to the right on the split // so we don't need to add one to offset when figuring // out where to plop list offset = GetIndexOf(parent,topChild); } // make a blockquote nsCOMPtr newBQ; res = CreateNode(bq, parent, offset, getter_AddRefs(newBQ)); if (NS_FAILED(res)) { nsEditor::EndTransaction(); return res; } // put a space in it so layout will draw the list item res = selection->Collapse(newBQ,0); if (NS_FAILED(res)) { nsEditor::EndTransaction(); return res; } nsAutoString theText(" "); res = InsertText(theText); if (NS_FAILED(res)) { nsEditor::EndTransaction(); return res; } // reposition selection to before the space character res = GetStartNodeAndOffset(selection, &node, &offset); if (NS_FAILED(res)) { nsEditor::EndTransaction(); return res; } res = selection->Collapse(node,0); if (NS_FAILED(res)) { nsEditor::EndTransaction(); return res; } } } nsEditor::EndTransaction(); // don't return this result! return NS_OK; } NS_IMETHODIMP nsHTMLEditor::Align(nsString& aAlignType) { nsresult res = nsEditor::BeginTransaction(); if (NS_FAILED(res)) return res; nsCOMPtr node; PRInt32 offset; // Find out if the selection is collapsed: nsCOMPtr selection; res = GetSelection(getter_AddRefs(selection)); if (NS_FAILED(res) || !selection) { // my kingdom for exceptions nsEditor::EndTransaction(); return res; } PRBool isCollapsed; res = selection->GetIsCollapsed(&isCollapsed); if (NS_FAILED(res)) { nsEditor::EndTransaction(); return res; } res = GetStartNodeAndOffset(selection, &node, &offset); if (!node) res = NS_ERROR_FAILURE; if (NS_FAILED(res)) { nsEditor::EndTransaction(); return res; } nsAutoString leftStr("left"); if (aAlignType == leftStr) { if (isCollapsed) { } } nsEditor::EndTransaction(); // don't return this result! return NS_OK; } NS_IMETHODIMP nsHTMLEditor::InsertList(nsString& aListType) { nsresult res = nsEditor::BeginTransaction(); if (NS_FAILED(res)) return res; // Find out if the selection is collapsed: nsCOMPtr selection; res = GetSelection(getter_AddRefs(selection)); if (NS_FAILED(res) || !selection) { // my kingdom for exceptions nsEditor::EndTransaction(); return res; } PRBool isCollapsed; res = selection->GetIsCollapsed(&isCollapsed); if (NS_FAILED(res)) { nsEditor::EndTransaction(); return res; } nsCOMPtr node; PRInt32 offset; res = GetStartNodeAndOffset(selection, &node, &offset); if (!node) res = NS_ERROR_FAILURE; if (NS_FAILED(res)) { nsEditor::EndTransaction(); return res; } if (isCollapsed) { // have to find a place to put the list nsCOMPtr parent = node; nsCOMPtr topChild = node; nsCOMPtr tmp; while ( !CanContainTag(parent, aListType)) { parent->GetParentNode(getter_AddRefs(tmp)); if (!tmp) { nsEditor::EndTransaction(); return NS_ERROR_FAILURE; } topChild = parent; parent = tmp; } if (parent != node) { // we need to split up to the child of parent res = SplitNodeDeep(topChild, node, offset); if (NS_FAILED(res)) { nsEditor::EndTransaction(); return res; } // topChild already went to the right on the split // so we don't need to add one to offset when figuring // out where to plop list offset = GetIndexOf(parent,topChild); } // make a list nsCOMPtr newList; res = CreateNode(aListType, parent, offset, getter_AddRefs(newList)); if (NS_FAILED(res)) { nsEditor::EndTransaction(); return res; } // make a list item nsAutoString tag("li"); nsCOMPtr newItem; res = CreateNode(tag, newList, 0, getter_AddRefs(newItem)); if (NS_FAILED(res)) { nsEditor::EndTransaction(); return res; } // put a space in it so layout will draw the list item res = selection->Collapse(newItem,0); if (NS_FAILED(res)) { nsEditor::EndTransaction(); return res; } nsAutoString theText(" "); res = InsertText(theText); if (NS_FAILED(res)) { nsEditor::EndTransaction(); return res; } // reposition selection to before the space character res = GetStartNodeAndOffset(selection, &node, &offset); if (NS_FAILED(res)) { nsEditor::EndTransaction(); return res; } res = selection->Collapse(node,0); if (NS_FAILED(res)) { nsEditor::EndTransaction(); return res; } } nsEditor::EndTransaction(); // don't return this result! return NS_OK; } NS_IMETHODIMP nsHTMLEditor::InsertLink(nsString& aURL) { nsresult res = nsEditor::BeginTransaction(); // Find out if the selection is collapsed: nsCOMPtr selection; res = GetSelection(getter_AddRefs(selection)); if (NS_FAILED(res) || !selection) { #ifdef DEBUG_akkana printf("Can't get selection!"); #endif return res; } PRBool isCollapsed; res = selection->GetIsCollapsed(&isCollapsed); if (NS_FAILED(res)) isCollapsed = PR_TRUE; // Temporary: we need to save the contents of the selection, // then insert them back in as the child of the newly created // anchor node in order to put the link around the selection. // This will require copying the selection into a document fragment, // then splicing the document fragment back into the tree after the // new anchor node has been put in place. As a temporary solution, // Copy/Paste does this for us in the text case // (and eventually in all cases). if (!isCollapsed) (void)Copy(); nsCOMPtr newNode; nsAutoString tag("A"); res = nsEditor::DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode)); if (NS_FAILED(res) || !newNode) return res; nsCOMPtr anchor (do_QueryInterface(newNode)); if (!anchor) { #ifdef DEBUG_akkana printf("Not an anchor element\n"); #endif return NS_NOINTERFACE; } res = anchor->SetHref(aURL); if (NS_FAILED(res)) { #ifdef DEBUG_akkana printf("SetHref failed"); #endif return res; } // Set the selection to the node we just inserted: res = GetSelection(getter_AddRefs(selection)); if (NS_FAILED(res) || !selection) { #ifdef DEBUG_akkana printf("Can't get selection!"); #endif return res; } res = selection->Collapse(newNode, 0); if (NS_FAILED(res)) { #ifdef DEBUG_akkana printf("Couldn't collapse"); #endif return res; } // If we weren't collapsed, paste the old selection back in under the link: if (!isCollapsed) (void)Paste(); // Otherwise (we were collapsed) insert some bogus text in // so the link will be visible else { nsString link("[***]"); (void) InsertText(link); // ignore return value -- we don't care } nsEditor::EndTransaction(); // don't return this result! return NS_OK; } NS_IMETHODIMP nsHTMLEditor::InsertImage(nsString& aURL, nsString& aWidth, nsString& aHeight, nsString& aHspace, nsString& aVspace, nsString& aBorder, nsString& aAlt, nsString& aAlignment) { nsresult res; nsCOMPtr newNode; nsCOMPtrdoc; res = GetDocument(getter_AddRefs(doc)); if (NS_SUCCEEDED(res)) { nsAutoString tag("IMG"); nsCOMPtrnewElement; res = doc->CreateElement(tag, getter_AddRefs(newElement)); if (NS_SUCCEEDED(res) && newElement) { newNode = do_QueryInterface(newElement); nsCOMPtr image (do_QueryInterface(newNode)); // Set all the attributes now, before we insert into the tree: if (image) { if (NS_SUCCEEDED(res = image->SetSrc(aURL))) if (NS_SUCCEEDED(res = image->SetWidth(aWidth))) if (NS_SUCCEEDED(res = image->SetHeight(aHeight))) if (NS_SUCCEEDED(res = image->SetAlt(aAlt))) if (NS_SUCCEEDED(res = image->SetBorder(aBorder))) if (NS_SUCCEEDED(res = image->SetAlign(aAlignment))) if (NS_SUCCEEDED(res = image->SetHspace(aHspace))) if (NS_SUCCEEDED(res = image->SetVspace(aVspace))) ; } } } // If any of these failed, then don't insert the new node into the tree if (NS_FAILED(res)) { #ifdef DEBUG_akkana printf("Some failure creating the new image node\n"); #endif return res; } // // Now we're ready to insert the new image node: // Starting now, don't return without ending the transaction! // (void)nsEditor::BeginTransaction(); nsCOMPtr parentNode; PRInt32 offsetOfNewNode; res = nsEditor::DeleteSelectionAndPrepareToCreateNode(parentNode, offsetOfNewNode); if (NS_SUCCEEDED(res)) { // and insert it into the right place in the tree: res = InsertNode(newNode, parentNode, offsetOfNewNode); } (void)nsEditor::EndTransaction(); // don't return this result! return res; } // This should replace InsertLink and InsertImage once it is working NS_IMETHODIMP nsHTMLEditor::GetSelectedElement(const nsString& aTagName, nsIDOMElement** aReturn) { if (!aReturn ) return NS_ERROR_NULL_POINTER; *aReturn = nsnull; nsAutoString TagName = aTagName; TagName.ToLowerCase(); //Note that this doesn't need to go through the transaction system nsresult result=NS_ERROR_NOT_INITIALIZED; PRBool first=PR_TRUE; nsCOMPtrselection; result = nsEditor::GetSelection(getter_AddRefs(selection)); if (NS_FAILED(result) || !selection) return result; PRBool isCollapsed; selection->GetIsCollapsed(&isCollapsed); nsCOMPtr selectedElement; PRBool bNodeFound = PR_FALSE; // Don't bother to examine selection if it is collapsed if (!isCollapsed) { nsCOMPtr enumerator; enumerator = do_QueryInterface(selection); if (enumerator) { enumerator->First(); nsISupports *currentItem; result = enumerator->CurrentItem(¤tItem); if ((NS_SUCCEEDED(result)) && currentItem) { nsCOMPtr range( do_QueryInterface(currentItem) ); nsCOMPtr 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 nsCOMPtr content; result = iter->CurrentNode(getter_AddRefs(content)); PRBool bOtherNodeTypeFound = PR_FALSE; while (NS_COMFALSE == iter->IsDone()) { // Query interface to cast nsIContent to nsIDOMNode // then get tagType to compare to aTagName // Clone node of each desired type and append it to the aDomFrag selectedElement = do_QueryInterface(content); if (selectedElement) { // If we already found a node, then we have another element, // so don't return an element if (bNodeFound) { bNodeFound; break; } nsAutoString domTagName; selectedElement->GetNodeName(domTagName); domTagName.ToLowerCase(); // The "A" tag is a pain, // used for both link(href is set) and "Named Anchor" if (aTagName == "href" || (aTagName == "anchor")) { // We could use GetAttribute, but might as well use anchor element directly nsCOMPtr anchor = do_QueryInterface(selectedElement); if (anchor) { nsString tmpText; if( aTagName == "href") { if (NS_SUCCEEDED(anchor->GetHref(tmpText)) && tmpText.GetUnicode() && tmpText.Length() != 0) bNodeFound = PR_TRUE; } else if (aTagName == "anchor") { if (NS_SUCCEEDED(anchor->GetName(tmpText)) && tmpText.GetUnicode() && tmpText.Length() != 0) bNodeFound = PR_TRUE; } } } else if (aTagName == domTagName) { // All other tag names are handled here bNodeFound = PR_TRUE; } if (!bNodeFound) { // Check if node we have is really part of the selection??? break; } } iter->Next(); } if (!bNodeFound) { char TagBuf[50] = ""; printf("No nodes of tag name = %s were found in selection\n", aTagName.ToCString(TagBuf, 50)); } } } else { // Should never get here? isCollapsed = PR_TRUE; printf("isCollapsed was FALSE, but no elements found in selection\n"); } } else { printf("Could not create enumerator for GetSelectionProperties\n"); } } if (bNodeFound) { *aReturn = selectedElement; } return result; } NS_IMETHODIMP nsHTMLEditor::CreateElementWithDefaults(const nsString& aTagName, nsIDOMElement** aReturn) { nsresult result=NS_ERROR_NOT_INITIALIZED; if (aReturn) *aReturn = nsnull; if (aTagName == "" || !aReturn) return NS_ERROR_NULL_POINTER; nsAutoString TagName = aTagName; TagName.ToLowerCase(); nsAutoString realTagName; PRBool isHREF = (TagName == "href"); PRBool isAnchor = (TagName == "anchor"); if (isHREF || isAnchor) { realTagName = "a"; } else { realTagName = TagName; } //We don't use editor's CreateElement because we don't want to // go through the transaction system nsCOMPtrnewElement; result = mDoc->CreateElement(realTagName, getter_AddRefs(newElement)); if (NS_FAILED(result) || !newElement) return NS_ERROR_FAILURE; // Set default values for new elements // SHOULD THIS BE PUT IN "RULES" SYSTEM OR // ATTRIBUTES SAVED IN PREFS? if (isAnchor) { // TODO: Get the text of the selection and build a suggested Name // Replace spaces with "_" } // ADD OTHER DEFAULT ATTRIBUTES HERE if (NS_SUCCEEDED(result)) { *aReturn = newElement; //do_QueryInterface(newElement); } return result; } NS_IMETHODIMP nsHTMLEditor::InsertElement(nsIDOMElement* aElement, PRBool aDeleteSelection, nsIDOMElement** aReturn) { nsresult result=NS_ERROR_NOT_INITIALIZED; if (aReturn) *aReturn = nsnull; if (!aElement || !aReturn) return NS_ERROR_NULL_POINTER; result = nsEditor::BeginTransaction(); if (NS_FAILED(result)) return result; nsCOMPtr parentSelectedNode; PRInt32 offsetOfNewNode; // Clear current selection. // Should put caret at anchor point? if (!aDeleteSelection) { nsCOMPtrselection; nsresult res = nsEditor::GetSelection(getter_AddRefs(selection)); if (NS_SUCCEEDED(res) && selection) { selection->ClearSelection(); } } nsAutoString tagName; // MAJOR KLUDGE -- CONVERT THE PLATFORM-SPECIFIC FORMAT INTO URL FORMAT // This should be done by the file-picker widget nsCOMPtr image = (do_QueryInterface(aElement)); if (image) { printf("INSERTING AN IMAGE\n"); nsAutoString src; image->GetSrc(src); nsFileSpec fileSpec(src); nsFileURL fileURL(fileSpec); } DeleteSelectionAndPrepareToCreateNode(parentSelectedNode, offsetOfNewNode); if (NS_SUCCEEDED(result)) { nsCOMPtr newNode = do_QueryInterface(aElement); result = InsertNode(aElement, parentSelectedNode, offsetOfNewNode); } (void)nsEditor::EndTransaction(); // 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 nsHTMLEditor::InsertLinkAroundSelection(nsIDOMElement* aAnchorElement) { nsresult result=NS_ERROR_UNEXPECTED; if (!aAnchorElement) return NS_ERROR_NULL_POINTER; // We must have a real selection nsCOMPtr selection; result = GetSelection(getter_AddRefs(selection)); if (NS_FAILED(result) || !selection) return result; PRBool isCollapsed; result = selection->GetIsCollapsed(&isCollapsed); if (NS_FAILED(result)) isCollapsed = PR_TRUE; if (isCollapsed) { printf("InsertLinkAroundSelection called but there is no selection!!!\n"); return NS_OK; } // Be sure we were given an anchor element nsCOMPtr anchor = do_QueryInterface(aAnchorElement); if (anchor) { nsAutoString href; if (NS_SUCCEEDED(anchor->GetHref(href)) && href.GetUnicode() && href.Length() > 0) { result = nsEditor::BeginTransaction(); if (NS_SUCCEEDED(result)) { const nsString attribute("href"); SetTextProperty(nsIEditProperty::a, &attribute, &href); //TODO: Enumerate through other properties of the anchor tag // and set those as well. // Optimization: Modify SetTextProperty to set all attributes at once? } (void)nsEditor::EndTransaction(); } } return result; } PRBool nsHTMLEditor::CanContainTag(nsIDOMNode* aParent, nsString &aTag) { if (!aParent) return PR_FALSE; static nsAutoString ulTag = "ul"; static nsAutoString olTag = "ol"; static nsAutoString liTag = "li"; static nsAutoString bodyTag = "body"; static nsAutoString tdTag = "td"; static nsAutoString thTag = "th"; static nsAutoString bqTag = "blockquote"; nsCOMPtr pTagAtom = GetTag(aParent); nsAutoString pTag; pTagAtom->ToString(pTag); // flesh this out... // for now, only lists and blockquotes are using this funct if (aTag.EqualsIgnoreCase(ulTag) || aTag.EqualsIgnoreCase(olTag) ) { if (pTag.EqualsIgnoreCase(bodyTag) || pTag.EqualsIgnoreCase(tdTag) || pTag.EqualsIgnoreCase(thTag) || pTag.EqualsIgnoreCase(ulTag) || pTag.EqualsIgnoreCase(olTag) || pTag.EqualsIgnoreCase(liTag) || pTag.EqualsIgnoreCase(bqTag) ) { return PR_TRUE; } } else if (aTag.EqualsIgnoreCase(bqTag) ) { if (pTag.EqualsIgnoreCase(bodyTag) || pTag.EqualsIgnoreCase(tdTag) || pTag.EqualsIgnoreCase(thTag) || pTag.EqualsIgnoreCase(liTag) || pTag.EqualsIgnoreCase(bqTag) ) { return PR_TRUE; } } return PR_FALSE; } NS_IMETHODIMP nsHTMLEditor::IsRootTag(nsString &aTag, PRBool &aIsTag) { static nsAutoString bodyTag = "body"; static nsAutoString tdTag = "td"; static nsAutoString thTag = "th"; static nsAutoString captionTag = "caption"; if (PR_TRUE==aTag.EqualsIgnoreCase(bodyTag) || PR_TRUE==aTag.EqualsIgnoreCase(tdTag) || PR_TRUE==aTag.EqualsIgnoreCase(thTag) || PR_TRUE==aTag.EqualsIgnoreCase(captionTag) ) { aIsTag = PR_TRUE; } else { aIsTag = PR_FALSE; } return NS_OK; } NS_IMETHODIMP nsHTMLEditor::IsSubordinateBlock(nsString &aTag, PRBool &aIsTag) { static nsAutoString p = "p"; static nsAutoString h1 = "h1"; static nsAutoString h2 = "h2"; static nsAutoString h3 = "h3"; static nsAutoString h4 = "h4"; static nsAutoString h5 = "h5"; static nsAutoString h6 = "h6"; static nsAutoString address = "address"; static nsAutoString pre = "pre"; static nsAutoString li = "li"; static nsAutoString dt = "dt"; static nsAutoString dd = "dd"; if (PR_TRUE==aTag.EqualsIgnoreCase(p) || PR_TRUE==aTag.EqualsIgnoreCase(h1) || PR_TRUE==aTag.EqualsIgnoreCase(h2) || PR_TRUE==aTag.EqualsIgnoreCase(h3) || PR_TRUE==aTag.EqualsIgnoreCase(h4) || PR_TRUE==aTag.EqualsIgnoreCase(h5) || PR_TRUE==aTag.EqualsIgnoreCase(h6) || PR_TRUE==aTag.EqualsIgnoreCase(address) || PR_TRUE==aTag.EqualsIgnoreCase(pre) || PR_TRUE==aTag.EqualsIgnoreCase(li) || PR_TRUE==aTag.EqualsIgnoreCase(dt) || PR_TRUE==aTag.EqualsIgnoreCase(dd) ) { aIsTag = PR_TRUE; } else { aIsTag = PR_FALSE; } return NS_OK; } NS_IMETHODIMP nsHTMLEditor::BeginComposition(void) { return nsTextEditor::BeginComposition(); } NS_IMETHODIMP nsHTMLEditor::EndComposition(void) { return nsTextEditor::EndComposition(); } NS_IMETHODIMP nsHTMLEditor::SetCompositionString(const nsString& aCompositionString) { return nsTextEditor::SetCompositionString(aCompositionString); }