diff --git a/editor/Makefile.in b/editor/Makefile.in index fcb921b25695..157f6d625c42 100644 --- a/editor/Makefile.in +++ b/editor/Makefile.in @@ -25,7 +25,7 @@ include $(DEPTH)/config/autoconf.mk DIRS = public ifdef MOZ_EDITOR -DIRS += base guimgr txmgr +DIRS += base guimgr txmgr ui endif include $(topsrcdir)/config/config.mk diff --git a/editor/base/nsEditor.cpp b/editor/base/nsEditor.cpp index 40447f7db645..b725694cca7f 100644 --- a/editor/base/nsEditor.cpp +++ b/editor/base/nsEditor.cpp @@ -23,9 +23,11 @@ #include "nsIDOMElement.h" #include "nsIDOMAttr.h" #include "nsIDOMNode.h" +#include "nsIDOMNamedNodeMap.h" #include "nsIDOMNodeList.h" #include "nsIDOMRange.h" #include "nsIDocument.h" +#include "nsVector.h" #include "nsIServiceManager.h" #include "nsEditFactory.h" #include "nsTextEditFactory.h" @@ -566,6 +568,79 @@ nsEditor::CreateTxnForRemoveAttribute(nsIDOMElement *aElement, return result; } +// Objects must be DOM elements +NS_IMETHODIMP +nsEditor::CopyAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode) +{ + nsresult result=NS_OK; + + if (!aDestNode || !aSourceNode) + return NS_ERROR_NULL_POINTER; + + nsCOMPtr destElement = do_QueryInterface(aDestNode); + nsCOMPtr sourceElement = do_QueryInterface(aSourceNode); + if (!destElement || !sourceElement) + return NS_ERROR_NO_INTERFACE; + + nsAutoString name; + nsAutoString value; + nsCOMPtr sourceAttributes; + sourceElement->GetAttributes(getter_AddRefs(sourceAttributes)); + nsCOMPtr destAttributes; + destElement->GetAttributes(getter_AddRefs(destAttributes)); + if (!sourceAttributes || !destAttributes) + return NS_ERROR_FAILURE; + + PRUint32 sourceCount; + sourceAttributes->GetLength(&sourceCount); + PRUint32 i, destCount; + destAttributes->GetLength(&destCount); + nsIDOMNode* attrNode; + + // Clear existing attributes + for (i = 0; i < destCount; i++) + { + if( NS_SUCCEEDED(destAttributes->Item(i, &attrNode)) && attrNode) + { + nsCOMPtr destAttribute = do_QueryInterface(attrNode); + if (destAttribute) + { + nsCOMPtr resultAttribute; + destElement->RemoveAttributeNode(destAttribute, getter_AddRefs(resultAttribute)); + // Is the resultAttribute deleted automagically? + } + } + } + // Set just the attributes that the source element has + for (i = 0; i < sourceCount; i++) + { + if( NS_SUCCEEDED(sourceAttributes->Item(i, &attrNode)) && attrNode) + { + nsCOMPtr sourceAttribute = do_QueryInterface(attrNode); + if (sourceAttribute) + { + nsString sourceAttrName; + if (NS_SUCCEEDED(sourceAttribute->GetName(sourceAttrName))) + { + nsString sourceAttrValue; + if (NS_SUCCEEDED(sourceAttribute->GetValue(sourceAttrValue)) && + sourceAttrValue != "") + { + destElement->SetAttribute(sourceAttrName, sourceAttrValue); + } else { + // Do we ever get here? + destElement->RemoveAttribute(sourceAttrName); +#if DEBUG_cmanske + printf("Attribute in NamedNodeMap has empty value in nsEditor::CopyAttributes()\n"); +#endif + } + } + } + } + } + return result; +} + NS_IMETHODIMP nsEditor::InsertBreak() { @@ -2132,7 +2207,6 @@ nsEditor::DebugDumpContent() const return NS_OK; } - //END nsEditor Private methods /* ----- TEST METHODS ----- */ diff --git a/editor/base/nsEditor.h b/editor/base/nsEditor.h index cd64b47d3155..30c78e6bcf95 100644 --- a/editor/base/nsEditor.h +++ b/editor/base/nsEditor.h @@ -108,6 +108,10 @@ public: NS_IMETHOD RemoveAttribute(nsIDOMElement *aElement, const nsString& aAttribute); + //NOTE: Most callers are dealing with Nodes, + // but these objects must supports nsIDOMElement + NS_IMETHOD CopyAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode); + NS_IMETHOD CreateNode(const nsString& aTag, nsIDOMNode * aParent, PRInt32 aPosition, diff --git a/editor/base/nsHTMLEditor.cpp b/editor/base/nsHTMLEditor.cpp index 88f04904a972..dedf844011d1 100644 --- a/editor/base/nsHTMLEditor.cpp +++ b/editor/base/nsHTMLEditor.cpp @@ -30,7 +30,11 @@ #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 "nsIComponentManager.h" #include "nsIServiceManager.h" @@ -49,6 +53,8 @@ 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; @@ -342,6 +348,12 @@ NS_IMETHODIMP nsHTMLEditor::OutputHTML(nsString& aOutputString) return nsTextEditor::OutputHTML(aOutputString); } +NS_IMETHODIMP +nsHTMLEditor::CopyAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode) +{ + return nsTextEditor::CopyAttributes(aDestNode, aSourceNode); +} + //================================================================ // HTML Editor methods // @@ -745,63 +757,239 @@ nsHTMLEditor::InsertImage(nsString& aURL, nsString& aAlignment) { nsresult res; + + (void)nsEditor::BeginTransaction(); + nsCOMPtr newNode; - - nsCOMPtrdoc; - res = GetDocument(getter_AddRefs(doc)); - if (NS_SUCCEEDED(res)) + nsAutoString tag("IMG"); + res = nsEditor::DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode)); + if (!NS_SUCCEEDED(res) || !newNode) { - 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_SUCCEEDED(res)) - { -#ifdef DEBUG_akkana - printf("Some failure creating the new image node\n"); -#endif + (void)nsEditor::EndTransaction(); 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)) + nsCOMPtr image (do_QueryInterface(newNode)); + if (!image) { - // and insert it into the right place in the tree: - res = InsertNode(newNode, parentNode, offsetOfNewNode); +#ifdef DEBUG_akkana + printf("Not an image element\n"); +#endif + (void)nsEditor::EndTransaction(); + return NS_NOINTERFACE; } + // Can't return from any of these intermediates + // because then we won't hit the EndTransaction() + 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))) + ; + (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_SUCCEEDED(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); + + // 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 && tmpText != "") + bNodeFound = PR_TRUE; + } else if (aTagName == "ANCHOR") + { + if (NS_SUCCEEDED(anchor->GetName(tmpText)) && tmpText && tmpText != "") + 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; + } + } + } + if (!bNodeFound) + printf("No nodes of tag name = %s were found in selection\n", aTagName); + } + } 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_SUCCEEDED(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; + + DeleteSelectionAndPrepareToCreateNode(parentSelectedNode, offsetOfNewNode); + if (NS_SUCCEEDED(result)) + { + nsCOMPtr newNode = do_QueryInterface(aElement); + + result = InsertNode(aElement, parentSelectedNode, offsetOfNewNode); + + } + (void)nsEditor::EndTransaction(); + + return result; +} diff --git a/editor/base/nsHTMLEditor.h b/editor/base/nsHTMLEditor.h index 3ed50cfe5509..5f5fc108ea7b 100644 --- a/editor/base/nsHTMLEditor.h +++ b/editor/base/nsHTMLEditor.h @@ -60,6 +60,7 @@ public: NS_IMETHOD DeleteSelection(nsIEditor::Direction aDir); NS_IMETHOD InsertText(const nsString& aStringToInsert); NS_IMETHOD InsertBreak(); + NS_IMETHOD CopyAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode); // Transaction control NS_IMETHOD EnableUndo(PRBool aEnable); @@ -70,7 +71,7 @@ public: NS_IMETHOD BeginTransaction(); NS_IMETHOD EndTransaction(); -// Selection and navigation -- exposed here for convenience +// Selection and navigation NS_IMETHOD MoveSelectionUp(nsIAtom *aIncrement, PRBool aExtendSelection); NS_IMETHOD MoveSelectionDown(nsIAtom *aIncrement, PRBool aExtendSelection); NS_IMETHOD MoveSelectionNext(nsIAtom *aIncrement, PRBool aExtendSelection); @@ -92,7 +93,8 @@ public: NS_IMETHOD OutputText(nsString& aOutputString); NS_IMETHOD OutputHTML(nsString& aOutputString); -//===================================== +// End of methods implemented in nsEditor +//============================================================= // HTML Editing methods NS_IMETHOD AddBlockParent(nsString& aParentTag); NS_IMETHOD RemoveBlockParent(); @@ -104,6 +106,11 @@ public: nsString& aBorder, nsString& aAlt, nsString& aAlignment); + // This should replace InsertLink and InsertImage once it is working + NS_IMETHOD GetSelectedElement(const nsString& aTagName, nsIDOMElement** aReturn); + NS_IMETHOD CreateElementWithDefaults(const nsString& aTagName, nsIDOMElement** aReturn); + NS_IMETHOD InsertElement(nsIDOMElement* aElement, PRBool aDeleteSelection, nsIDOMElement** aReturn); + // Table Editing (implemented in EditTable.cpp) NS_IMETHOD CreateTxnForInsertTable(const nsIDOMElement *aTableNode, InsertTableTxn ** aTxn); NS_IMETHOD GetColIndexForCell(nsIDOMNode *aCellNode, PRInt32 &aCellIndex); diff --git a/editor/base/nsTextEditor.cpp b/editor/base/nsTextEditor.cpp index bfbdf8fa5b97..0b5964317940 100644 --- a/editor/base/nsTextEditor.cpp +++ b/editor/base/nsTextEditor.cpp @@ -2341,3 +2341,10 @@ nsTextEditor::SetTypeInStateForProperty(TypeInState &aTypeInState, else { return NS_ERROR_FAILURE; } return NS_OK; } + +// This file should be rearranged to put all methods that simply call nsEditor together +NS_IMETHODIMP +nsTextEditor::CopyAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode) +{ + return nsEditor::CopyAttributes(aDestNode, aSourceNode); +} diff --git a/editor/base/nsTextEditor.h b/editor/base/nsTextEditor.h index 5136d225a44f..4529148c5d80 100644 --- a/editor/base/nsTextEditor.h +++ b/editor/base/nsTextEditor.h @@ -52,6 +52,8 @@ public: //Initialization NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell); +//============================================================================ +// Methods that are duplicates of nsEditor -- exposed here for convenience // Editing Operations NS_IMETHOD SetTextProperty(nsIAtom *aProperty, const nsString *aAttribute, @@ -64,6 +66,7 @@ public: NS_IMETHOD DeleteSelection(nsIEditor::Direction aDir); NS_IMETHOD InsertText(const nsString& aStringToInsert); NS_IMETHOD InsertBreak(); + NS_IMETHOD CopyAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode); // Transaction control NS_IMETHOD EnableUndo(PRBool aEnable); @@ -96,6 +99,8 @@ public: NS_IMETHOD OutputText(nsString& aOutputString); NS_IMETHOD OutputHTML(nsString& aOutputString); +// End of methods implemented in nsEditor +//============================================================= protected: diff --git a/editor/dialogs/EditorDialog.css b/editor/dialogs/EditorDialog.css index 3785aa0f3cb1..dcc5417a5df6 100644 --- a/editor/dialogs/EditorDialog.css +++ b/editor/dialogs/EditorDialog.css @@ -1,21 +1,18 @@ /* XUL ELEMENTS */ window, WINDOW { display: block; - width: 100%; +/* width: 100%; */ background: silver; background-color: #CCCCCC; /* not working on Macintosh */ padding: 5px; font-family: Sans-Serif; - font-size: 8pt; + font-size: 10pt; } -/* Defeat the default behavior of borderless until rollover */ -button { border: 2px outset } - -/* values = margin, padding. Padding doesn't work yet */ -checkbox, CHECKBOX { margin: 5px 3px } - -radio, RADIO { margin: 5px 3px } +radio, RADIO, checkbox, CHECKBOX { + margin-top: 5px; + margin-right: 3px; +} tree, TREE { display: table; @@ -30,23 +27,25 @@ tree, TREE { /* Force all rows to line up cell contents at the top */ tr, TR { vertical-align: top } -/* FOR DEBUG PURPOSE -- SHOW CELL BORDER */ -td, TD { border-width: 0px; border-style: inset } +/* FOR DEBUG PURPOSE -- SHOW CELL BORDER +td, TD { border-width: 1px; padding: 0px; border-style: outset } + +table, TABLE { border-width: 1px; padding: 0px; border-style: inset } +*/ -table, TABLE { border-width: 0px } p, br, td { font-family: Sans-Serif; - font-size: 8pt; - margin-top: 4px; - margin-bottom: 4px + font-size: 10pt; + margin: 0px; } + fieldset, FIELDSET { - border-width: 3px; + border-width: 2px; border-style: groove; - padding: 3px; - margin: 5px; + padding: 2px; + margin: 0px; text-align: left; } @@ -54,70 +53,30 @@ legend, LEGEND { font-family: Sans-Serif; font-size: 10pt; border-width: 0; + width: 100% + height: 100% +/* BUG??? align can't be set - must use in the HTML */ text-align: left; } -/* CLASSES */ -[CLASS~=GrooveBorder] { border: 2px groove; padding: 3px } -[CLASS~=RidgeBorder] { border: 2px ridge; padding: 3px } +/* CLASSES */ +[CLASS~=GrooveBorder] { border: 2px groove; padding: 3px } -[CLASS~=OutsetBorder] { border: 1px outset; padding: 3px } +[CLASS~=RidgeBorder] { border: 2px ridge; padding: 3px } -[CLASS~=InsetBorder] { border: 1px inset; padding: 3px } +[CLASS~=OutsetBorder] { border: 1px outset; padding: 3px } + +[CLASS~=InsetBorder] { border: 1px inset; padding: 3px } + +/* +3/31/99 These don't work because of a bug HYATT knows about +.GrooveBorder { border: 2px groove; padding: 3px } +.RidgeBorder { border: 2px ridge; padding: 3px } +.OutsetBorder { border: 1px outset; padding: 3px } +.InsetBorder { border: 1px inset; padding: 3px } .tableCenterAlign { vertical-align: middle; -} - -.genericbutton { - width: 80px; - height: 20px; - margin-top: 5px; - margin-bottom: 5px; -} - -/* IDs only one button per dialog */ -#okbutton { - width: 80px; - height: 20px; - left: 385px; - top: 320px; - align: right; - font-style: normal; position: absolute; visibility: visible; z-index: auto -} - -#cancelbutton { - width: 80px; - height: 20px; - left: 295px; - top: 320px; - align: right; - font-style: normal; position: absolute; visibility: visible; z-index: auto -} - -#applybutton { - width: 80px; - height: 20px; - left: 205px; - top: 320px; - align: right; font-style: normal; position: absolute; visibility: visible; z-index: auto -} - -#helpbutton { - width: 80px; - height: 20px; - left: 10px; - top: 320px; - align : left; - font-style: normal; position: absolute; visibility: visible; z-index: auto -} - -#extrahtmlbutton { - width: 80px; - height: 20px; - align: right; - left: 385px; - font-style: normal; position: absolute; visibility: visible; z-index: auto -} +*/ \ No newline at end of file diff --git a/editor/libeditor/base/nsEditor.cpp b/editor/libeditor/base/nsEditor.cpp index 40447f7db645..b725694cca7f 100644 --- a/editor/libeditor/base/nsEditor.cpp +++ b/editor/libeditor/base/nsEditor.cpp @@ -23,9 +23,11 @@ #include "nsIDOMElement.h" #include "nsIDOMAttr.h" #include "nsIDOMNode.h" +#include "nsIDOMNamedNodeMap.h" #include "nsIDOMNodeList.h" #include "nsIDOMRange.h" #include "nsIDocument.h" +#include "nsVector.h" #include "nsIServiceManager.h" #include "nsEditFactory.h" #include "nsTextEditFactory.h" @@ -566,6 +568,79 @@ nsEditor::CreateTxnForRemoveAttribute(nsIDOMElement *aElement, return result; } +// Objects must be DOM elements +NS_IMETHODIMP +nsEditor::CopyAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode) +{ + nsresult result=NS_OK; + + if (!aDestNode || !aSourceNode) + return NS_ERROR_NULL_POINTER; + + nsCOMPtr destElement = do_QueryInterface(aDestNode); + nsCOMPtr sourceElement = do_QueryInterface(aSourceNode); + if (!destElement || !sourceElement) + return NS_ERROR_NO_INTERFACE; + + nsAutoString name; + nsAutoString value; + nsCOMPtr sourceAttributes; + sourceElement->GetAttributes(getter_AddRefs(sourceAttributes)); + nsCOMPtr destAttributes; + destElement->GetAttributes(getter_AddRefs(destAttributes)); + if (!sourceAttributes || !destAttributes) + return NS_ERROR_FAILURE; + + PRUint32 sourceCount; + sourceAttributes->GetLength(&sourceCount); + PRUint32 i, destCount; + destAttributes->GetLength(&destCount); + nsIDOMNode* attrNode; + + // Clear existing attributes + for (i = 0; i < destCount; i++) + { + if( NS_SUCCEEDED(destAttributes->Item(i, &attrNode)) && attrNode) + { + nsCOMPtr destAttribute = do_QueryInterface(attrNode); + if (destAttribute) + { + nsCOMPtr resultAttribute; + destElement->RemoveAttributeNode(destAttribute, getter_AddRefs(resultAttribute)); + // Is the resultAttribute deleted automagically? + } + } + } + // Set just the attributes that the source element has + for (i = 0; i < sourceCount; i++) + { + if( NS_SUCCEEDED(sourceAttributes->Item(i, &attrNode)) && attrNode) + { + nsCOMPtr sourceAttribute = do_QueryInterface(attrNode); + if (sourceAttribute) + { + nsString sourceAttrName; + if (NS_SUCCEEDED(sourceAttribute->GetName(sourceAttrName))) + { + nsString sourceAttrValue; + if (NS_SUCCEEDED(sourceAttribute->GetValue(sourceAttrValue)) && + sourceAttrValue != "") + { + destElement->SetAttribute(sourceAttrName, sourceAttrValue); + } else { + // Do we ever get here? + destElement->RemoveAttribute(sourceAttrName); +#if DEBUG_cmanske + printf("Attribute in NamedNodeMap has empty value in nsEditor::CopyAttributes()\n"); +#endif + } + } + } + } + } + return result; +} + NS_IMETHODIMP nsEditor::InsertBreak() { @@ -2132,7 +2207,6 @@ nsEditor::DebugDumpContent() const return NS_OK; } - //END nsEditor Private methods /* ----- TEST METHODS ----- */ diff --git a/editor/libeditor/base/nsEditor.h b/editor/libeditor/base/nsEditor.h index cd64b47d3155..30c78e6bcf95 100644 --- a/editor/libeditor/base/nsEditor.h +++ b/editor/libeditor/base/nsEditor.h @@ -108,6 +108,10 @@ public: NS_IMETHOD RemoveAttribute(nsIDOMElement *aElement, const nsString& aAttribute); + //NOTE: Most callers are dealing with Nodes, + // but these objects must supports nsIDOMElement + NS_IMETHOD CopyAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode); + NS_IMETHOD CreateNode(const nsString& aTag, nsIDOMNode * aParent, PRInt32 aPosition, diff --git a/editor/libeditor/html/nsHTMLEditor.cpp b/editor/libeditor/html/nsHTMLEditor.cpp index 88f04904a972..dedf844011d1 100644 --- a/editor/libeditor/html/nsHTMLEditor.cpp +++ b/editor/libeditor/html/nsHTMLEditor.cpp @@ -30,7 +30,11 @@ #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 "nsIComponentManager.h" #include "nsIServiceManager.h" @@ -49,6 +53,8 @@ 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; @@ -342,6 +348,12 @@ NS_IMETHODIMP nsHTMLEditor::OutputHTML(nsString& aOutputString) return nsTextEditor::OutputHTML(aOutputString); } +NS_IMETHODIMP +nsHTMLEditor::CopyAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode) +{ + return nsTextEditor::CopyAttributes(aDestNode, aSourceNode); +} + //================================================================ // HTML Editor methods // @@ -745,63 +757,239 @@ nsHTMLEditor::InsertImage(nsString& aURL, nsString& aAlignment) { nsresult res; + + (void)nsEditor::BeginTransaction(); + nsCOMPtr newNode; - - nsCOMPtrdoc; - res = GetDocument(getter_AddRefs(doc)); - if (NS_SUCCEEDED(res)) + nsAutoString tag("IMG"); + res = nsEditor::DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode)); + if (!NS_SUCCEEDED(res) || !newNode) { - 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_SUCCEEDED(res)) - { -#ifdef DEBUG_akkana - printf("Some failure creating the new image node\n"); -#endif + (void)nsEditor::EndTransaction(); 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)) + nsCOMPtr image (do_QueryInterface(newNode)); + if (!image) { - // and insert it into the right place in the tree: - res = InsertNode(newNode, parentNode, offsetOfNewNode); +#ifdef DEBUG_akkana + printf("Not an image element\n"); +#endif + (void)nsEditor::EndTransaction(); + return NS_NOINTERFACE; } + // Can't return from any of these intermediates + // because then we won't hit the EndTransaction() + 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))) + ; + (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_SUCCEEDED(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); + + // 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 && tmpText != "") + bNodeFound = PR_TRUE; + } else if (aTagName == "ANCHOR") + { + if (NS_SUCCEEDED(anchor->GetName(tmpText)) && tmpText && tmpText != "") + 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; + } + } + } + if (!bNodeFound) + printf("No nodes of tag name = %s were found in selection\n", aTagName); + } + } 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_SUCCEEDED(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; + + DeleteSelectionAndPrepareToCreateNode(parentSelectedNode, offsetOfNewNode); + if (NS_SUCCEEDED(result)) + { + nsCOMPtr newNode = do_QueryInterface(aElement); + + result = InsertNode(aElement, parentSelectedNode, offsetOfNewNode); + + } + (void)nsEditor::EndTransaction(); + + return result; +} diff --git a/editor/libeditor/html/nsHTMLEditor.h b/editor/libeditor/html/nsHTMLEditor.h index 3ed50cfe5509..5f5fc108ea7b 100644 --- a/editor/libeditor/html/nsHTMLEditor.h +++ b/editor/libeditor/html/nsHTMLEditor.h @@ -60,6 +60,7 @@ public: NS_IMETHOD DeleteSelection(nsIEditor::Direction aDir); NS_IMETHOD InsertText(const nsString& aStringToInsert); NS_IMETHOD InsertBreak(); + NS_IMETHOD CopyAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode); // Transaction control NS_IMETHOD EnableUndo(PRBool aEnable); @@ -70,7 +71,7 @@ public: NS_IMETHOD BeginTransaction(); NS_IMETHOD EndTransaction(); -// Selection and navigation -- exposed here for convenience +// Selection and navigation NS_IMETHOD MoveSelectionUp(nsIAtom *aIncrement, PRBool aExtendSelection); NS_IMETHOD MoveSelectionDown(nsIAtom *aIncrement, PRBool aExtendSelection); NS_IMETHOD MoveSelectionNext(nsIAtom *aIncrement, PRBool aExtendSelection); @@ -92,7 +93,8 @@ public: NS_IMETHOD OutputText(nsString& aOutputString); NS_IMETHOD OutputHTML(nsString& aOutputString); -//===================================== +// End of methods implemented in nsEditor +//============================================================= // HTML Editing methods NS_IMETHOD AddBlockParent(nsString& aParentTag); NS_IMETHOD RemoveBlockParent(); @@ -104,6 +106,11 @@ public: nsString& aBorder, nsString& aAlt, nsString& aAlignment); + // This should replace InsertLink and InsertImage once it is working + NS_IMETHOD GetSelectedElement(const nsString& aTagName, nsIDOMElement** aReturn); + NS_IMETHOD CreateElementWithDefaults(const nsString& aTagName, nsIDOMElement** aReturn); + NS_IMETHOD InsertElement(nsIDOMElement* aElement, PRBool aDeleteSelection, nsIDOMElement** aReturn); + // Table Editing (implemented in EditTable.cpp) NS_IMETHOD CreateTxnForInsertTable(const nsIDOMElement *aTableNode, InsertTableTxn ** aTxn); NS_IMETHOD GetColIndexForCell(nsIDOMNode *aCellNode, PRInt32 &aCellIndex); diff --git a/editor/makefile.win b/editor/makefile.win index 8aad143e4a47..c4e37f9178b6 100644 --- a/editor/makefile.win +++ b/editor/makefile.win @@ -21,7 +21,7 @@ IGNORE_MANIFEST=1 !if defined(DISABLE_EDITOR) DIRS= public !else -DIRS= public base guimgr txmgr +DIRS= public base guimgr txmgr ui !endif include <$(DEPTH)\config\rules.mak> diff --git a/editor/public/nsIHTMLEditor.h b/editor/public/nsIHTMLEditor.h index fb94cb2bb80c..2b1276d11901 100644 --- a/editor/public/nsIHTMLEditor.h +++ b/editor/public/nsIHTMLEditor.h @@ -27,6 +27,7 @@ #include "nsIEditor.h" #include "nscore.h" +#include "nsIDOMDocumentFragment.h" class nsIEditorCallback; class nsISupportsArray; @@ -115,6 +116,11 @@ public: nsString& aBorder, nsString& aAlt, nsString& aAlignment)=0; + // This should replace InsertLink and InsertImage once it is working + NS_IMETHOD GetSelectedElement(const nsString& aTagName, nsIDOMElement** aReturn)=0; + NS_IMETHOD CreateElementWithDefaults(const nsString& aTagName, nsIDOMElement** aReturn)=0; + NS_IMETHOD InsertElement(nsIDOMElement* aElement, PRBool aDeleteSelection, nsIDOMElement** aReturn)=0; + // Table editing Methods NS_IMETHOD InsertTable()=0; NS_IMETHOD InsertTableCell(PRInt32 aNumber, PRBool aAfter)=0; diff --git a/editor/ui/dialogs/content/EdLinkProps.js b/editor/ui/dialogs/content/EdLinkProps.js new file mode 100644 index 000000000000..d338b3c35faa --- /dev/null +++ b/editor/ui/dialogs/content/EdLinkProps.js @@ -0,0 +1,106 @@ + var appCore; + var toolkitCore; + + var editElement = null; + var insertNew = true; + + // NOTE: Use "HREF" instead of "A" to distinguish from Named Anchor + // The returned nodes will have the "A" tagName + var tagName = "HREF"; + + // dialog initialization code + function Startup() { + dump("Doing Startup...\n"); + toolkitCore = GetToolkitCore(); + if(!toolkitCore) { + dump("toolkitCore not found!!! And we can't close the dialog!\n"); + } + // NEVER create an appcore here - we must find parent editor's + appCore = XPAppCoresManager.Find("EditorAppCoreHTML"); + if(!appCore || !toolkitCore) { + dump("EditorAppCore not found!!!\n"); + toolkitCore.CloseWindow(); + } + dump("EditorAppCore found for Link Properties dialog\n"); + + // Get a single selected element and edit its properites + editElement = appCore.getSelectedElement(tagName); + + if (editElement) { + // We found an element and don't need to insert it + insertNew = false; + } else { + // We don't have an element selected, + // so create one with default attributes + dump("Element not selected - calling createElementWithDefaults\n"); + editElement = appCore.createElementWithDefaults(tagName); + } + + if(!editElement) + { + dump("Failed to get selected element or create a new one!\n"); + toolkitCore.CloseWindow(window); + } + dump("GetSelectedElement...\n"); + + hrefInput = document.getElementById("textHREF"); + // Set the input element value to current HREF + if (hrefInput) + { + dump("Setting HREF editbox value\n"); + hrefInput.value = editNode.href; + } + } + + function applyChanges() { + // Set the input element value to current HREF + hrefInput = document.getElementById("textHREF"); + if (hrefInput) + { + dump("Copying edit field HREF value to node attribute\n"); + editElement.setAttribute("href",hrefInput.value); + } + // Get text to use for a new link + if (insertNew) + { + textLink = document.getElementById("textLink"); + if (textLink) + { + // Append the link text as the last child node + // of the docFrag + textNode = appCore.editorDocument.createTextNode(textLink.value); + if (textNode) + { + editElement.appendChild(textNode); + dump("Text for new link appended to HREF node\n"); + newElement = appCore.insertElement(editElement, true); + if (newElement != editElement) + { + dump("Returned element from insertElement is different from orginal element.\n"); + } + } + } + // Once inserted, we can modify properties, but don't insert again + insertNew = false; + } + } + + function onOK() { + applyChanges(); + toolkitCore.CloseWindow(window); + } + + function onCancel() { + dump("Calling CloseWindow...\n"); + toolkitCore.CloseWindow(window); + } + + function GetToolkitCore() { + var toolkitCore = XPAppCoresManager.Find("ToolkitCore"); + if (!toolkitCore) { + toolkitCore = new ToolkitCore(); + if (toolkitCore) + toolkitCore.Init("ToolkitCore"); + } + return toolkitCore; + } diff --git a/editor/ui/dialogs/content/EdLinkProps.xul b/editor/ui/dialogs/content/EdLinkProps.xul index 246eea95a64a..a9c7d953c804 100644 --- a/editor/ui/dialogs/content/EdLinkProps.xul +++ b/editor/ui/dialogs/content/EdLinkProps.xul @@ -1,120 +1,13 @@ - + - - var appCore; - var toolkitCore; - - var editElement = null; - var insertNew = true; - - // NOTE: Use "HREF" instead of "A" to distinguish from Named Anchor - // The returned nodes will have the "A" tagName - var tagName = "HREF"; - - // dialog initialization code - function Startup() { - dump("Doing Startup...\n"); - toolkitCore = GetToolkitCore(); - if(!toolkitCore) { - dump("toolkitCore not found!!! And we can't close the dialog!\n"); - } - // NEVER create an appcore here - we must find parent editor's - appCore = XPAppCoresManager.Find("EditorAppCoreHTML"); - if(!appCore || !toolkitCore) { - dump("EditorAppCore not found!!!\n"); - toolkitCore.CloseWindow(); - } - dump("EditorAppCore found for Link Properties dialog\n"); - - // Get a single selected element and edit its properites - editElement = appCore.getSelectedElement(tagName); - - if (editElement) { - // We found an element and don't need to insert it - insertNew = false; - } else { - // We don't have an element selected, - // so create one with default attributes - dump("Element not selected - calling createElementWithDefaults\n"); - editElement = appCore.createElementWithDefaults(tagName); - } - - if(!editElement) - { - dump("Failed to get selected element or create a new one!\n"); - toolkitCore.CloseWindow(window); - } - dump("GetSelectedElement...\n"); - - hrefInput = document.getElementById("textHREF"); - // Set the input element value to current HREF - if (hrefInput) - { - dump("Setting HREF editbox value\n"); - hrefInput.value = editNode.href; - } - } - - function applyChanges() { - // Set the input element value to current HREF - hrefInput = document.getElementById("textHREF"); - if (hrefInput) - { - dump("Copying edit field HREF value to node attribute\n"); - editElement.setAttribute("href",hrefInput.value); - } - // Get text to use for a new link - if (insertNew) - { - textLink = document.getElementById("textLink"); - if (textLink) - { - // Append the link text as the last child node - // of the docFrag - textNode = appCore.editorDocument.createTextNode(textLink.value); - if (textNode) - { - editElement.appendChild(textNode); - dump("Text for new link appended to HREF node\n"); - newElement = appCore.insertElement(editElement); - if (newElement != editElement) - { - dump("Returned element from insertElement is different from orginal element.\n"); - } - } - } - // Once inserted, we can modify properties, but don't insert again - insertNew = false; - } - } - - function onOK() { - applyChanges(); - toolkitCore.CloseWindow(window); - } - - function onCancel() { - dump("Calling CloseWindow...\n"); - toolkitCore.CloseWindow(window); - } - - function GetToolkitCore() { - var toolkitCore = XPAppCoresManager.Find("ToolkitCore"); - if (!toolkitCore) { - toolkitCore = new ToolkitCore(); - if (toolkitCore) - toolkitCore.Init("ToolkitCore"); - } - return toolkitCore; - } - +