deleting the last editable object in a document now causes a bogus placeholder <P>&nbsp;</P> to be added to the document. This node has the

attribute MOZ_EDITOR_BOGUS_NODE set to TRUE.  this solves the problem of when you do a SelectAll then a DEL, you don't have a cursor
or any way to set selection in the document. You do still have the problem that the nbsp is pretty narrow and the frame containing it is hard to hit
with the mouse.  We really need the "set selection to frame nearest the mouse down point" operation.

the intent is for all inserts to look for this node, and if found delete it before the insert.  likewise, all deletes will be no-ops if this node is found.
This commit is contained in:
buster%netscape.com 1999-03-12 02:28:24 +00:00
parent 9450d290ae
commit b615bb95b3
8 changed files with 259 additions and 83 deletions

View File

@ -752,7 +752,7 @@ NS_IMETHODIMP nsEditor::SelectAll()
if (NS_SUCCEEDED(result) && selection)
{
nsCOMPtr<nsIDOMNodeList>nodeList;
nsString bodyTag = "body";
nsAutoString bodyTag = "body";
nsresult result = mDoc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList));
if ((NS_SUCCEEDED(result)) && nodeList)
{
@ -846,9 +846,9 @@ nsString & nsIEditor::GetTextNodeTag()
NS_IMETHODIMP nsEditor::CreateNode(const nsString& aTag,
nsIDOMNode * aParent,
PRInt32 aPosition,
nsIDOMNode ** aNewNode)
nsIDOMNode * aParent,
PRInt32 aPosition,
nsIDOMNode ** aNewNode)
{
CreateElementTxn *txn;
nsresult result = CreateTxnForCreateElement(aTag, aParent, aPosition, &txn);
@ -1075,7 +1075,7 @@ NS_IMETHODIMP nsEditor::DoInitialInsert(const nsString & aStringToInsert)
BeginTransaction();
nsCOMPtr<nsIDOMNodeList>nodeList;
nsString bodyTag = "body";
nsAutoString bodyTag = "body";
nsresult result = mDoc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList));
if ((NS_SUCCEEDED(result)) && nodeList)
{

View File

@ -17,16 +17,17 @@
*/
#include "nsTextEditRules.h"
#include "nsTextEditor.h"
#include "nsCOMPtr.h"
#include "nsIDOMNode.h"
#include "nsIDOMElement.h"
#include "nsIDOMNodeList.h"
#include "nsIDOMSelection.h"
#include "nsIDOMRange.h"
#include "nsIDOMCharacterData.h"
#include "nsIEnumerator.h"
NS_IMPL_ADDREF(nsTextEditRules)
NS_IMPL_RELEASE(nsTextEditRules)
static char* kMOZEditorBogusNode="MOZ_EDITOR_BOGUS_NODE";
static PRBool NodeIsType(nsIDOMNode *aNode, nsIAtom *aTag)
{
@ -46,23 +47,25 @@ static PRBool NodeIsType(nsIDOMNode *aNode, nsIAtom *aTag)
nsTextEditRules::nsTextEditRules()
{
NS_INIT_REFCNT();
mEditor = nsnull;
}
nsTextEditRules::~nsTextEditRules()
{
// do NOT delete mEditor here. We do not hold a ref count to mEditor. mEditor owns our lifespan.
}
NS_IMETHODIMP
nsTextEditRules::Init(nsIEditor *aEditor, nsIEditRules *aNextRule)
nsTextEditRules::Init(nsTextEditor *aEditor)
{
// null aNextRule is ok
if (!aEditor) { return NS_ERROR_NULL_POINTER; }
mEditor = do_QueryInterface(aEditor);
mNextRule = do_QueryInterface(aNextRule);
mEditor = aEditor; // we hold a non-refcounted reference back to our editor
return NS_OK;
}
NS_IMETHODIMP
nsTextEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel)
{
@ -78,7 +81,7 @@ nsTextEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel)
NS_IMETHODIMP
nsTextEditRules::DidInsertBreak(nsIDOMSelection *aSelection, nsresult aResult)
{
nsresult result;
nsresult result = aResult; // if aResult is an error, we return it.
if (!aSelection) { return NS_ERROR_NULL_POINTER; }
PRBool isCollapsed;
aSelection->IsCollapsed(&isCollapsed);
@ -183,25 +186,99 @@ nsTextEditRules::GetInsertBreakTag(nsIAtom **aTag)
return NS_OK;
}
NS_IMETHODIMP
nsTextEditRules::QueryInterface(REFNSIID aIID, void** aInstancePtr)
nsTextEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, PRBool *aCancel)
{
if (NULL == aInstancePtr) {
return NS_ERROR_NULL_POINTER;
if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; }
// initialize out param
*aCancel = PR_FALSE;
// any prep work would go here
return NS_OK;
}
// if the document is empty, insert a bogus text node with a &nbsp;
NS_IMETHODIMP
nsTextEditRules::DidDeleteSelection(nsIDOMSelection *aSelection, nsresult aResult)
{
nsresult result = aResult; // if aResult is an error, we just return it
if (!aSelection) { return NS_ERROR_NULL_POINTER; }
PRBool isCollapsed;
aSelection->IsCollapsed(&isCollapsed);
NS_ASSERTION(PR_TRUE==isCollapsed, "selection not collapsed after delete selection.");
// if the delete selection resulted in no content
// insert a special bogus text node with a &nbsp; character in it.
if (NS_SUCCEEDED(aResult)) // note we're checking aResult, the param that tells us if the insert break happened or not
{
nsCOMPtr<nsIDOMDocument>doc;
mEditor->GetDocument(getter_AddRefs(doc));
nsCOMPtr<nsIDOMNodeList>nodeList;
nsAutoString bodyTag = "body";
nsresult result = doc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList));
if ((NS_SUCCEEDED(result)) && nodeList)
{
PRUint32 count;
nodeList->GetLength(&count);
NS_ASSERTION(1==count, "there is not exactly 1 body in the document!");
nsCOMPtr<nsIDOMNode>bodyNode;
result = nodeList->Item(0, getter_AddRefs(bodyNode));
if ((NS_SUCCEEDED(result)) && bodyNode)
{ // now we've got the body tag.
// iterate the body tag, looking for editable content
// if no editable content is found, insert the bogus node
PRBool needsBogusContent=PR_TRUE;
nsCOMPtr<nsIDOMNode>bodyChild;
result = bodyNode->GetFirstChild(getter_AddRefs(bodyChild));
while ((NS_SUCCEEDED(result)) && bodyChild)
{ //XXX: wrongly assumes that all content is editable
//XXX: needs a "IsEditableNode(node) method
needsBogusContent = PR_FALSE;
break;
/*
nsCOMPtr<nsIDOMNode>temp;
bodyChild=>GetNextSibling(getter_AddRefs(temp));
bodyChild = do_QueryInterface(temp);
*/
}
if (PR_TRUE==needsBogusContent)
{
nsCOMPtr<nsIDOMNode>newPNode;
result = mEditor->CreateNode(nsAutoString("P"), bodyNode, 0,
getter_AddRefs(newPNode));
if ((NS_SUCCEEDED(result)) && newPNode)
{
nsCOMPtr<nsIDOMNode>newTNode;
result = mEditor->CreateNode(nsIEditor::GetTextNodeTag(), newPNode, 0,
getter_AddRefs(newTNode));
if ((NS_SUCCEEDED(result)) && newTNode)
{
nsCOMPtr<nsIDOMCharacterData>newNodeAsText;
newNodeAsText = do_QueryInterface(newTNode);
if (newNodeAsText)
{
nsAutoString data;
data += 160;
newNodeAsText->SetData(data);
aSelection->Collapse(newTNode, 0);
}
}
// make sure we know the PNode is bogus
nsCOMPtr<nsIDOMElement>newPElement;
newPElement = do_QueryInterface(newPNode);
if (newPElement)
{
newPElement->SetAttribute(kMOZEditorBogusNode, "TRUE");
}
}
}
}
}
}
if (aIID.Equals(nsISupports::GetIID())) {
*aInstancePtr = (void*)(nsISupports*)this;
NS_ADDREF_THIS();
return NS_OK;
}
if (aIID.Equals(nsIEditRules::GetIID())) {
*aInstancePtr = (void*)(nsIEditRules*)this;
NS_ADDREF_THIS();
return NS_OK;
}
*aInstancePtr = 0;
return NS_NOINTERFACE;
return result;
}

View File

@ -19,10 +19,11 @@
#ifndef nsTextEditRules_h__
#define nsTextEditRules_h__
#include "nsIEditRules.h"
#include "nsIEditor.h"
#include "nsCOMPtr.h"
class nsTextEditor;
/** Object that encapsulates HTML text-specific editing rules.
*
@ -35,25 +36,25 @@
* 2. Selection must not be explicitly set by the rule method.
* Any manipulation of Selection must be done by the editor.
*/
class nsTextEditRules : public nsIEditRules
class nsTextEditRules
{
public:
NS_DECL_ISUPPORTS
nsTextEditRules();
virtual ~nsTextEditRules();
NS_IMETHOD Init(nsIEditor *aEditor, nsIEditRules *aNextRule);
NS_IMETHOD Init(nsTextEditor *aEditor);
NS_IMETHOD WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel);
NS_IMETHOD DidInsertBreak(nsIDOMSelection *aSelection, nsresult aResult);
NS_IMETHOD GetInsertBreakTag(nsIAtom **aTag);
NS_IMETHOD WillDeleteSelection(nsIDOMSelection *aSelection, PRBool *aCancel);
NS_IMETHOD DidDeleteSelection(nsIDOMSelection *aSelection, nsresult aResult);
protected:
nsCOMPtr<nsIEditor> mEditor;
nsCOMPtr<nsIEditRules> mNextRule;
nsTextEditor *mEditor; // note that we do not refcount the editor
};

View File

@ -64,12 +64,9 @@ class nsIFrame;
#include <strstrea.h>
#endif
#include "CreateElementTxn.h"
#include "nsIComponentManager.h"
#include "nsIServiceManager.h"
// until there is a rules factory...
#include "nsTextEditRules.h"
@ -91,6 +88,7 @@ nsTextEditor::nsTextEditor()
{
// Done in nsEditor
// NS_INIT_REFCNT();
mRules = nsnull;
}
nsTextEditor::~nsTextEditor()
@ -115,6 +113,9 @@ nsTextEditor::~nsTextEditor()
else
NS_NOTREACHED("~nsTextEditor");
}
if (mRules) {
delete mRules;
}
}
// Adds appropriate AddRef, Release, and QueryInterface methods for derived class
@ -180,11 +181,9 @@ NS_IMETHODIMP nsTextEditor::Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell)
//erP->AddEventListener(mMouseListenerP, kIDOMMouseListenerIID);
// instantiate the rules for this text editor
// XXX: we should get the rules from a factory
// XXX: we should be told which set of rules to instantiate
nsIEditRules *rules = (nsIEditRules *) new nsTextEditRules();
rules->Init((nsIEditor *)this, nsnull);
mRules = do_QueryInterface(rules);
mRules = new nsTextEditRules();
mRules->Init(this);
result = NS_OK;
@ -380,7 +379,28 @@ NS_IMETHODIMP nsTextEditor::RemoveTextProperty(nsIAtom *aProperty)
NS_IMETHODIMP nsTextEditor::DeleteSelection(nsIEditor::Direction aDir)
{
return nsEditor::DeleteSelection(aDir);
if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
nsCOMPtr<nsIDOMSelection> selection;
PRBool cancel= PR_FALSE;
nsresult result = nsEditor::BeginTransaction();
if (NS_FAILED(result)) { return result; }
// pre-process
nsEditor::GetSelection(getter_AddRefs(selection));
result = mRules->WillDeleteSelection(selection, &cancel);
if ((PR_FALSE==cancel) && (NS_SUCCEEDED(result)))
{
result = nsEditor::DeleteSelection(aDir);
// post-process
result = mRules->DidDeleteSelection(selection, result);
}
nsresult endTxnResult = nsEditor::EndTransaction(); // don't return this result!
NS_ASSERTION ((NS_SUCCEEDED(endTxnResult)), "bad end transaction result");
return result;
}
NS_IMETHODIMP nsTextEditor::InsertText(const nsString& aStringToInsert)
@ -431,7 +451,7 @@ NS_IMETHODIMP nsTextEditor::InsertBreak()
}
}
nsresult endTxnResult = nsEditor::EndTransaction(); // don't return this result!
NS_ASSERTION ((NS_SUCCEEDED(result)), "bad end transaction result");
NS_ASSERTION ((NS_SUCCEEDED(endTxnResult)), "bad end transaction result");
return result;
}
@ -705,10 +725,10 @@ NS_IMETHODIMP nsTextEditor::OutputHTML(nsString& aOutputString)
NS_IMETHODIMP nsTextEditor::SetTextPropertiesForNode(nsIDOMNode *aNode,
nsIDOMNode *aParent,
PRInt32 aStartOffset,
PRInt32 aEndOffset,
nsIAtom *aPropName)
nsIDOMNode *aParent,
PRInt32 aStartOffset,
PRInt32 aEndOffset,
nsIAtom *aPropName)
{
nsresult result=NS_OK;
nsCOMPtr<nsIDOMCharacterData>nodeAsChar;

View File

@ -23,7 +23,7 @@
#include "nsCOMPtr.h"
#include "nsIDOMEventListener.h"
#include "nsEditor.h"
#include "nsIEditRules.h"
#include "nsTextEditRules.h"
class nsIStyleContext;
class nsIDOMRange;
@ -121,7 +121,7 @@ protected:
// Data members
protected:
nsCOMPtr<nsIEditRules> mRules;
nsTextEditRules* mRules;
nsCOMPtr<nsIDOMEventListener> mKeyListenerP;
nsCOMPtr<nsIDOMEventListener> mMouseListenerP;

View File

@ -752,7 +752,7 @@ NS_IMETHODIMP nsEditor::SelectAll()
if (NS_SUCCEEDED(result) && selection)
{
nsCOMPtr<nsIDOMNodeList>nodeList;
nsString bodyTag = "body";
nsAutoString bodyTag = "body";
nsresult result = mDoc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList));
if ((NS_SUCCEEDED(result)) && nodeList)
{
@ -846,9 +846,9 @@ nsString & nsIEditor::GetTextNodeTag()
NS_IMETHODIMP nsEditor::CreateNode(const nsString& aTag,
nsIDOMNode * aParent,
PRInt32 aPosition,
nsIDOMNode ** aNewNode)
nsIDOMNode * aParent,
PRInt32 aPosition,
nsIDOMNode ** aNewNode)
{
CreateElementTxn *txn;
nsresult result = CreateTxnForCreateElement(aTag, aParent, aPosition, &txn);
@ -1075,7 +1075,7 @@ NS_IMETHODIMP nsEditor::DoInitialInsert(const nsString & aStringToInsert)
BeginTransaction();
nsCOMPtr<nsIDOMNodeList>nodeList;
nsString bodyTag = "body";
nsAutoString bodyTag = "body";
nsresult result = mDoc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList));
if ((NS_SUCCEEDED(result)) && nodeList)
{

View File

@ -17,16 +17,17 @@
*/
#include "nsTextEditRules.h"
#include "nsTextEditor.h"
#include "nsCOMPtr.h"
#include "nsIDOMNode.h"
#include "nsIDOMElement.h"
#include "nsIDOMNodeList.h"
#include "nsIDOMSelection.h"
#include "nsIDOMRange.h"
#include "nsIDOMCharacterData.h"
#include "nsIEnumerator.h"
NS_IMPL_ADDREF(nsTextEditRules)
NS_IMPL_RELEASE(nsTextEditRules)
static char* kMOZEditorBogusNode="MOZ_EDITOR_BOGUS_NODE";
static PRBool NodeIsType(nsIDOMNode *aNode, nsIAtom *aTag)
{
@ -46,23 +47,25 @@ static PRBool NodeIsType(nsIDOMNode *aNode, nsIAtom *aTag)
nsTextEditRules::nsTextEditRules()
{
NS_INIT_REFCNT();
mEditor = nsnull;
}
nsTextEditRules::~nsTextEditRules()
{
// do NOT delete mEditor here. We do not hold a ref count to mEditor. mEditor owns our lifespan.
}
NS_IMETHODIMP
nsTextEditRules::Init(nsIEditor *aEditor, nsIEditRules *aNextRule)
nsTextEditRules::Init(nsTextEditor *aEditor)
{
// null aNextRule is ok
if (!aEditor) { return NS_ERROR_NULL_POINTER; }
mEditor = do_QueryInterface(aEditor);
mNextRule = do_QueryInterface(aNextRule);
mEditor = aEditor; // we hold a non-refcounted reference back to our editor
return NS_OK;
}
NS_IMETHODIMP
nsTextEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel)
{
@ -78,7 +81,7 @@ nsTextEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel)
NS_IMETHODIMP
nsTextEditRules::DidInsertBreak(nsIDOMSelection *aSelection, nsresult aResult)
{
nsresult result;
nsresult result = aResult; // if aResult is an error, we return it.
if (!aSelection) { return NS_ERROR_NULL_POINTER; }
PRBool isCollapsed;
aSelection->IsCollapsed(&isCollapsed);
@ -183,25 +186,99 @@ nsTextEditRules::GetInsertBreakTag(nsIAtom **aTag)
return NS_OK;
}
NS_IMETHODIMP
nsTextEditRules::QueryInterface(REFNSIID aIID, void** aInstancePtr)
nsTextEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, PRBool *aCancel)
{
if (NULL == aInstancePtr) {
return NS_ERROR_NULL_POINTER;
if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; }
// initialize out param
*aCancel = PR_FALSE;
// any prep work would go here
return NS_OK;
}
// if the document is empty, insert a bogus text node with a &nbsp;
NS_IMETHODIMP
nsTextEditRules::DidDeleteSelection(nsIDOMSelection *aSelection, nsresult aResult)
{
nsresult result = aResult; // if aResult is an error, we just return it
if (!aSelection) { return NS_ERROR_NULL_POINTER; }
PRBool isCollapsed;
aSelection->IsCollapsed(&isCollapsed);
NS_ASSERTION(PR_TRUE==isCollapsed, "selection not collapsed after delete selection.");
// if the delete selection resulted in no content
// insert a special bogus text node with a &nbsp; character in it.
if (NS_SUCCEEDED(aResult)) // note we're checking aResult, the param that tells us if the insert break happened or not
{
nsCOMPtr<nsIDOMDocument>doc;
mEditor->GetDocument(getter_AddRefs(doc));
nsCOMPtr<nsIDOMNodeList>nodeList;
nsAutoString bodyTag = "body";
nsresult result = doc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList));
if ((NS_SUCCEEDED(result)) && nodeList)
{
PRUint32 count;
nodeList->GetLength(&count);
NS_ASSERTION(1==count, "there is not exactly 1 body in the document!");
nsCOMPtr<nsIDOMNode>bodyNode;
result = nodeList->Item(0, getter_AddRefs(bodyNode));
if ((NS_SUCCEEDED(result)) && bodyNode)
{ // now we've got the body tag.
// iterate the body tag, looking for editable content
// if no editable content is found, insert the bogus node
PRBool needsBogusContent=PR_TRUE;
nsCOMPtr<nsIDOMNode>bodyChild;
result = bodyNode->GetFirstChild(getter_AddRefs(bodyChild));
while ((NS_SUCCEEDED(result)) && bodyChild)
{ //XXX: wrongly assumes that all content is editable
//XXX: needs a "IsEditableNode(node) method
needsBogusContent = PR_FALSE;
break;
/*
nsCOMPtr<nsIDOMNode>temp;
bodyChild=>GetNextSibling(getter_AddRefs(temp));
bodyChild = do_QueryInterface(temp);
*/
}
if (PR_TRUE==needsBogusContent)
{
nsCOMPtr<nsIDOMNode>newPNode;
result = mEditor->CreateNode(nsAutoString("P"), bodyNode, 0,
getter_AddRefs(newPNode));
if ((NS_SUCCEEDED(result)) && newPNode)
{
nsCOMPtr<nsIDOMNode>newTNode;
result = mEditor->CreateNode(nsIEditor::GetTextNodeTag(), newPNode, 0,
getter_AddRefs(newTNode));
if ((NS_SUCCEEDED(result)) && newTNode)
{
nsCOMPtr<nsIDOMCharacterData>newNodeAsText;
newNodeAsText = do_QueryInterface(newTNode);
if (newNodeAsText)
{
nsAutoString data;
data += 160;
newNodeAsText->SetData(data);
aSelection->Collapse(newTNode, 0);
}
}
// make sure we know the PNode is bogus
nsCOMPtr<nsIDOMElement>newPElement;
newPElement = do_QueryInterface(newPNode);
if (newPElement)
{
newPElement->SetAttribute(kMOZEditorBogusNode, "TRUE");
}
}
}
}
}
}
if (aIID.Equals(nsISupports::GetIID())) {
*aInstancePtr = (void*)(nsISupports*)this;
NS_ADDREF_THIS();
return NS_OK;
}
if (aIID.Equals(nsIEditRules::GetIID())) {
*aInstancePtr = (void*)(nsIEditRules*)this;
NS_ADDREF_THIS();
return NS_OK;
}
*aInstancePtr = 0;
return NS_NOINTERFACE;
return result;
}

View File

@ -19,10 +19,11 @@
#ifndef nsTextEditRules_h__
#define nsTextEditRules_h__
#include "nsIEditRules.h"
#include "nsIEditor.h"
#include "nsCOMPtr.h"
class nsTextEditor;
/** Object that encapsulates HTML text-specific editing rules.
*
@ -35,25 +36,25 @@
* 2. Selection must not be explicitly set by the rule method.
* Any manipulation of Selection must be done by the editor.
*/
class nsTextEditRules : public nsIEditRules
class nsTextEditRules
{
public:
NS_DECL_ISUPPORTS
nsTextEditRules();
virtual ~nsTextEditRules();
NS_IMETHOD Init(nsIEditor *aEditor, nsIEditRules *aNextRule);
NS_IMETHOD Init(nsTextEditor *aEditor);
NS_IMETHOD WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel);
NS_IMETHOD DidInsertBreak(nsIDOMSelection *aSelection, nsresult aResult);
NS_IMETHOD GetInsertBreakTag(nsIAtom **aTag);
NS_IMETHOD WillDeleteSelection(nsIDOMSelection *aSelection, PRBool *aCancel);
NS_IMETHOD DidDeleteSelection(nsIDOMSelection *aSelection, nsresult aResult);
protected:
nsCOMPtr<nsIEditor> mEditor;
nsCOMPtr<nsIEditRules> mNextRule;
nsTextEditor *mEditor; // note that we do not refcount the editor
};