fixed bug 20613 & 21121: both invloved newlines in preformatted text. Layout gives no frames for blanklines caused by newlines, so you cant click or arrow to them. I replace such newlines with breaks now.

This commit is contained in:
jfrancis%netscape.com 2000-01-13 10:17:35 +00:00
Родитель 6222e5412c
Коммит a5fc421d20
10 изменённых файлов: 382 добавлений и 18 удалений

Просмотреть файл

@ -98,12 +98,17 @@ nsHTMLEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags)
bodyNode = do_QueryInterface(bodyElem);
if (bodyNode)
{
// temporarily turn off rules sniffing
nsAutoLockRulesSniffing lockIt((nsTextEditRules*)this);
res = nsComponentManager::CreateInstance(kRangeCID, nsnull, NS_GET_IID(nsIDOMRange),
getter_AddRefs(mDocChangeRange));
if (NS_FAILED(res)) return res;
if (!mDocChangeRange) return NS_ERROR_NULL_POINTER;
mDocChangeRange->SelectNode(bodyNode);
AdjustSpecialBreaks();
res = ReplaceNewlines(mDocChangeRange);
if (NS_FAILED(res)) return res;
res = AdjustSpecialBreaks();
if (NS_FAILED(res)) return res;
}
// turn on undo
mEditor->EnableUndo(PR_TRUE);
@ -180,6 +185,13 @@ nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection)
res = AdjustWhitespace(selection);
if (NS_FAILED(res)) return res;
}
// replace newlines that are preformatted
if ((action == nsEditor::kOpInsertText) ||
(action == nsEditor::kOpInsertIMEText) ||
(action == nsEditor::kOpInsertNode))
{
res = ReplaceNewlines(mDocChangeRange);
}
// clean up any empty nodes in the selection
res = RemoveEmptyNodes();
if (NS_FAILED(res)) return res;
@ -1577,8 +1589,27 @@ nsHTMLEditRules::WillAlign(nsIDOMSelection *aSelection,
PRBool outMakeEmpty;
res = ShouldMakeEmptyBlock(aSelection, alignType, &outMakeEmpty);
if (NS_FAILED(res)) return res;
if (outMakeEmpty) return NS_OK;
if (outMakeEmpty)
{
PRInt32 offset;
nsCOMPtr<nsIDOMNode> brNode, parent, theDiv;
nsAutoString divType("div");
res = mEditor->GetStartNodeAndOffset(aSelection, &parent, &offset);
if (NS_FAILED(res)) return res;
res = mEditor->CreateNode(divType, parent, offset, getter_AddRefs(theDiv));
if (NS_FAILED(res)) return res;
// set up the alignment on the div
nsCOMPtr<nsIDOMElement> divElem = do_QueryInterface(theDiv);
nsAutoString attr("align");
res = mEditor->SetAttribute(divElem, attr, *alignType);
if (NS_FAILED(res)) return res;
*aHandled = PR_TRUE;
// put in a moz-br so that it won't get deleted
res = CreateMozBR(theDiv, 0, &brNode);
if (NS_FAILED(res)) return res;
res = aSelection->Collapse(theDiv, 0);
return res;
}
// convert the selection ranges into "promoted" selection ranges:
// this basically just expands the range to include the immediate

Просмотреть файл

@ -4269,6 +4269,38 @@ nsHTMLEditor::CanContainTag(nsIDOMNode* aParent, const nsString &aTag)
}
NS_IMETHODIMP
nsHTMLEditor::SelectEntireDocument(nsIDOMSelection *aSelection)
{
nsresult res;
if (!aSelection || !mRules) { return NS_ERROR_NULL_POINTER; }
// get body node
nsCOMPtr<nsIDOMElement>bodyElement;
res = GetBodyElement(getter_AddRefs(bodyElement));
if (NS_FAILED(res)) return res;
nsCOMPtr<nsIDOMNode>bodyNode = do_QueryInterface(bodyElement);
if (!bodyNode) return NS_ERROR_FAILURE;
// is doc empty?
PRBool bDocIsEmpty;
res = mRules->DocumentIsEmpty(&bDocIsEmpty);
if (NS_FAILED(res)) return res;
if (bDocIsEmpty)
{
// if its empty dont select entire doc - that would select the bogus node
return aSelection->Collapse(bodyNode, 0);
}
else
{
return nsEditor::SelectEntireDocument(aSelection);
}
return res;
}
#ifdef XP_MAC
#pragma mark -
#pragma mark --- Random methods ---

Просмотреть файл

@ -241,11 +241,6 @@ public:
NS_IMETHOD DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed);
/* ------------ nsICSSLoaderObserver -------------- */
NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet*aSheet, PRBool aNotify);
/* ------------ nsEditor overrides ---------------- */
/** All editor operations which alter the doc should be prefaced
* with a call to StartOperation, naming the action and direction */
NS_IMETHOD StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection);
@ -257,6 +252,12 @@ public:
/** returns PR_TRUE if aParent can contain a child of type aTag */
PRBool CanContainTag(nsIDOMNode* aParent, const nsString &aTag);
/** make the given selection span the entire document */
NS_IMETHOD SelectEntireDocument(nsIDOMSelection *aSelection);
/* ------------ nsICSSLoaderObserver -------------- */
NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet*aSheet, PRBool aNotify);
/* ------------ Utility Routines, not part of public API -------------- */
NS_IMETHOD GetBodyStyleContext(nsIStyleContext** aStyleContext);

Просмотреть файл

@ -37,8 +37,10 @@
#include "nsLayoutCID.h"
#include "nsIEditProperty.h"
#include "nsEditorUtils.h"
#include "EditTxn.h"
static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID);
static NS_DEFINE_CID(kContentIteratorCID, NS_CONTENTITERATOR_CID);
static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
#define CANCEL_OPERATION_IF_READONLY_OR_DISABLED \
@ -82,6 +84,24 @@ nsTextEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags)
mEditor->GetSelection(getter_AddRefs(selection));
NS_ASSERTION(selection, "editor cannot get selection");
nsresult res = CreateBogusNodeIfNeeded(selection); // this method handles null selection, which should never happen anyway
// create a range that is the entire body contents
if (NS_FAILED(res)) return res;
nsCOMPtr<nsIDOMElement> bodyElement;
res = mEditor->GetBodyElement(getter_AddRefs(bodyElement));
if (NS_FAILED(res)) return res;
if (!bodyElement) return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsIDOMNode>bodyNode = do_QueryInterface(bodyElement);
if (!bodyNode) return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMRange> wholeDoc;
res = nsComponentManager::CreateInstance(kRangeCID, nsnull, NS_GET_IID(nsIDOMRange),
getter_AddRefs(wholeDoc));
if (NS_FAILED(res)) return res;
res = wholeDoc->SelectNode(bodyNode);
if (NS_FAILED(res)) return res;
// replace newlines in that range with breaks
res = ReplaceNewlines(wholeDoc);
return res;
}
@ -1264,6 +1284,101 @@ nsTextEditRules::DidOutputText(nsIDOMSelection *aSelection, nsresult aResult)
}
nsresult
nsTextEditRules::ReplaceNewlines(nsIDOMRange *aRange)
{
if (!aRange) return NS_ERROR_NULL_POINTER;
// convert any newlines in editable, preformatted text nodes
// into normal breaks. this is because layout wont give us a place
// to put the cursor on empty lines otherwise.
nsCOMPtr<nsIContentIterator> iter;
nsCOMPtr<nsISupports> isupports;
PRUint32 nodeCount,j;
nsCOMPtr<nsISupportsArray> arrayOfNodes;
// make an isupportsArray to hold a list of nodes
nsresult res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes));
if (NS_FAILED(res)) return res;
// need an iterator
res = nsComponentManager::CreateInstance(kContentIteratorCID,
nsnull,
NS_GET_IID(nsIContentIterator),
getter_AddRefs(iter));
if (NS_FAILED(res)) return res;
res = iter->Init(aRange);
if (NS_FAILED(res)) return res;
// gather up a list of editable preformatted text nodes
while (NS_ENUMERATOR_FALSE == iter->IsDone())
{
nsCOMPtr<nsIDOMNode> node;
nsCOMPtr<nsIContent> content;
res = iter->CurrentNode(getter_AddRefs(content));
if (NS_FAILED(res)) return res;
node = do_QueryInterface(content);
if (!node) return NS_ERROR_FAILURE;
if (mEditor->IsTextNode(node) && mEditor->IsEditable(node))
{
PRBool isPRE;
res = mEditor->IsPreformatted(node, &isPRE);
if (NS_FAILED(res)) return res;
if (isPRE)
{
isupports = do_QueryInterface(node);
arrayOfNodes->AppendElement(isupports);
}
}
res = iter->Next();
if (NS_FAILED(res)) return res;
}
// replace newlines with breaks. have to do this left to right,
// since inserting the break can split the text node, and the
// original node becomes the righthand node.
char newlineChar[] = {'\n',0};
res = arrayOfNodes->Count(&nodeCount);
if (NS_FAILED(res)) return res;
for (j = 0; j < nodeCount; j++)
{
isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0));
nsCOMPtr<nsIDOMNode> brNode, theNode( do_QueryInterface(isupports) );
nsCOMPtr<nsIDOMCharacterData> textNode( do_QueryInterface(theNode) );
arrayOfNodes->RemoveElementAt(0);
// find the newline
PRInt32 offset;
nsAutoString tempString;
do
{
textNode->GetData(tempString);
offset = tempString.FindCharInSet(newlineChar);
if (offset == -1) break; // done with this node
// delete the newline
EditTxn *txn;
// note 1: we are not telling edit listeners about these because they don't care
// note 2: we are not wrapping these in a placeholder because we know they already are,
// or, failing that, undo is disabled
res = mEditor->CreateTxnForDeleteText(textNode, offset, 1, (DeleteTextTxn**)&txn);
if (NS_FAILED(res)) return res;
if (!txn) return NS_ERROR_OUT_OF_MEMORY;
res = mEditor->Do(txn);
if (NS_FAILED(res)) return res;
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
// insert a break
res = mEditor->CreateBR(textNode, offset, &brNode);
if (NS_FAILED(res)) return res;
} while (1); // break used to exit while loop
}
return res;
}
nsresult
nsTextEditRules::CreateBogusNodeIfNeeded(nsIDOMSelection *aSelection)
{

Просмотреть файл

@ -182,6 +182,9 @@ protected:
const nsString &aValue,
nsIDOMSelection *aSelection);
/** replaces newllines with breaks, if needed. acts on doc portion in aRange */
nsresult ReplaceNewlines(nsIDOMRange *aRange);
/** creates a bogus text node if the document has no editable content */
nsresult CreateBogusNodeIfNeeded(nsIDOMSelection *aSelection);

Просмотреть файл

@ -98,12 +98,17 @@ nsHTMLEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags)
bodyNode = do_QueryInterface(bodyElem);
if (bodyNode)
{
// temporarily turn off rules sniffing
nsAutoLockRulesSniffing lockIt((nsTextEditRules*)this);
res = nsComponentManager::CreateInstance(kRangeCID, nsnull, NS_GET_IID(nsIDOMRange),
getter_AddRefs(mDocChangeRange));
if (NS_FAILED(res)) return res;
if (!mDocChangeRange) return NS_ERROR_NULL_POINTER;
mDocChangeRange->SelectNode(bodyNode);
AdjustSpecialBreaks();
res = ReplaceNewlines(mDocChangeRange);
if (NS_FAILED(res)) return res;
res = AdjustSpecialBreaks();
if (NS_FAILED(res)) return res;
}
// turn on undo
mEditor->EnableUndo(PR_TRUE);
@ -180,6 +185,13 @@ nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection)
res = AdjustWhitespace(selection);
if (NS_FAILED(res)) return res;
}
// replace newlines that are preformatted
if ((action == nsEditor::kOpInsertText) ||
(action == nsEditor::kOpInsertIMEText) ||
(action == nsEditor::kOpInsertNode))
{
res = ReplaceNewlines(mDocChangeRange);
}
// clean up any empty nodes in the selection
res = RemoveEmptyNodes();
if (NS_FAILED(res)) return res;
@ -1577,8 +1589,27 @@ nsHTMLEditRules::WillAlign(nsIDOMSelection *aSelection,
PRBool outMakeEmpty;
res = ShouldMakeEmptyBlock(aSelection, alignType, &outMakeEmpty);
if (NS_FAILED(res)) return res;
if (outMakeEmpty) return NS_OK;
if (outMakeEmpty)
{
PRInt32 offset;
nsCOMPtr<nsIDOMNode> brNode, parent, theDiv;
nsAutoString divType("div");
res = mEditor->GetStartNodeAndOffset(aSelection, &parent, &offset);
if (NS_FAILED(res)) return res;
res = mEditor->CreateNode(divType, parent, offset, getter_AddRefs(theDiv));
if (NS_FAILED(res)) return res;
// set up the alignment on the div
nsCOMPtr<nsIDOMElement> divElem = do_QueryInterface(theDiv);
nsAutoString attr("align");
res = mEditor->SetAttribute(divElem, attr, *alignType);
if (NS_FAILED(res)) return res;
*aHandled = PR_TRUE;
// put in a moz-br so that it won't get deleted
res = CreateMozBR(theDiv, 0, &brNode);
if (NS_FAILED(res)) return res;
res = aSelection->Collapse(theDiv, 0);
return res;
}
// convert the selection ranges into "promoted" selection ranges:
// this basically just expands the range to include the immediate

Просмотреть файл

@ -4269,6 +4269,38 @@ nsHTMLEditor::CanContainTag(nsIDOMNode* aParent, const nsString &aTag)
}
NS_IMETHODIMP
nsHTMLEditor::SelectEntireDocument(nsIDOMSelection *aSelection)
{
nsresult res;
if (!aSelection || !mRules) { return NS_ERROR_NULL_POINTER; }
// get body node
nsCOMPtr<nsIDOMElement>bodyElement;
res = GetBodyElement(getter_AddRefs(bodyElement));
if (NS_FAILED(res)) return res;
nsCOMPtr<nsIDOMNode>bodyNode = do_QueryInterface(bodyElement);
if (!bodyNode) return NS_ERROR_FAILURE;
// is doc empty?
PRBool bDocIsEmpty;
res = mRules->DocumentIsEmpty(&bDocIsEmpty);
if (NS_FAILED(res)) return res;
if (bDocIsEmpty)
{
// if its empty dont select entire doc - that would select the bogus node
return aSelection->Collapse(bodyNode, 0);
}
else
{
return nsEditor::SelectEntireDocument(aSelection);
}
return res;
}
#ifdef XP_MAC
#pragma mark -
#pragma mark --- Random methods ---

Просмотреть файл

@ -241,11 +241,6 @@ public:
NS_IMETHOD DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed);
/* ------------ nsICSSLoaderObserver -------------- */
NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet*aSheet, PRBool aNotify);
/* ------------ nsEditor overrides ---------------- */
/** All editor operations which alter the doc should be prefaced
* with a call to StartOperation, naming the action and direction */
NS_IMETHOD StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection);
@ -257,6 +252,12 @@ public:
/** returns PR_TRUE if aParent can contain a child of type aTag */
PRBool CanContainTag(nsIDOMNode* aParent, const nsString &aTag);
/** make the given selection span the entire document */
NS_IMETHOD SelectEntireDocument(nsIDOMSelection *aSelection);
/* ------------ nsICSSLoaderObserver -------------- */
NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet*aSheet, PRBool aNotify);
/* ------------ Utility Routines, not part of public API -------------- */
NS_IMETHOD GetBodyStyleContext(nsIStyleContext** aStyleContext);

Просмотреть файл

@ -37,8 +37,10 @@
#include "nsLayoutCID.h"
#include "nsIEditProperty.h"
#include "nsEditorUtils.h"
#include "EditTxn.h"
static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID);
static NS_DEFINE_CID(kContentIteratorCID, NS_CONTENTITERATOR_CID);
static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
#define CANCEL_OPERATION_IF_READONLY_OR_DISABLED \
@ -82,6 +84,24 @@ nsTextEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags)
mEditor->GetSelection(getter_AddRefs(selection));
NS_ASSERTION(selection, "editor cannot get selection");
nsresult res = CreateBogusNodeIfNeeded(selection); // this method handles null selection, which should never happen anyway
// create a range that is the entire body contents
if (NS_FAILED(res)) return res;
nsCOMPtr<nsIDOMElement> bodyElement;
res = mEditor->GetBodyElement(getter_AddRefs(bodyElement));
if (NS_FAILED(res)) return res;
if (!bodyElement) return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsIDOMNode>bodyNode = do_QueryInterface(bodyElement);
if (!bodyNode) return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMRange> wholeDoc;
res = nsComponentManager::CreateInstance(kRangeCID, nsnull, NS_GET_IID(nsIDOMRange),
getter_AddRefs(wholeDoc));
if (NS_FAILED(res)) return res;
res = wholeDoc->SelectNode(bodyNode);
if (NS_FAILED(res)) return res;
// replace newlines in that range with breaks
res = ReplaceNewlines(wholeDoc);
return res;
}
@ -1264,6 +1284,101 @@ nsTextEditRules::DidOutputText(nsIDOMSelection *aSelection, nsresult aResult)
}
nsresult
nsTextEditRules::ReplaceNewlines(nsIDOMRange *aRange)
{
if (!aRange) return NS_ERROR_NULL_POINTER;
// convert any newlines in editable, preformatted text nodes
// into normal breaks. this is because layout wont give us a place
// to put the cursor on empty lines otherwise.
nsCOMPtr<nsIContentIterator> iter;
nsCOMPtr<nsISupports> isupports;
PRUint32 nodeCount,j;
nsCOMPtr<nsISupportsArray> arrayOfNodes;
// make an isupportsArray to hold a list of nodes
nsresult res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes));
if (NS_FAILED(res)) return res;
// need an iterator
res = nsComponentManager::CreateInstance(kContentIteratorCID,
nsnull,
NS_GET_IID(nsIContentIterator),
getter_AddRefs(iter));
if (NS_FAILED(res)) return res;
res = iter->Init(aRange);
if (NS_FAILED(res)) return res;
// gather up a list of editable preformatted text nodes
while (NS_ENUMERATOR_FALSE == iter->IsDone())
{
nsCOMPtr<nsIDOMNode> node;
nsCOMPtr<nsIContent> content;
res = iter->CurrentNode(getter_AddRefs(content));
if (NS_FAILED(res)) return res;
node = do_QueryInterface(content);
if (!node) return NS_ERROR_FAILURE;
if (mEditor->IsTextNode(node) && mEditor->IsEditable(node))
{
PRBool isPRE;
res = mEditor->IsPreformatted(node, &isPRE);
if (NS_FAILED(res)) return res;
if (isPRE)
{
isupports = do_QueryInterface(node);
arrayOfNodes->AppendElement(isupports);
}
}
res = iter->Next();
if (NS_FAILED(res)) return res;
}
// replace newlines with breaks. have to do this left to right,
// since inserting the break can split the text node, and the
// original node becomes the righthand node.
char newlineChar[] = {'\n',0};
res = arrayOfNodes->Count(&nodeCount);
if (NS_FAILED(res)) return res;
for (j = 0; j < nodeCount; j++)
{
isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0));
nsCOMPtr<nsIDOMNode> brNode, theNode( do_QueryInterface(isupports) );
nsCOMPtr<nsIDOMCharacterData> textNode( do_QueryInterface(theNode) );
arrayOfNodes->RemoveElementAt(0);
// find the newline
PRInt32 offset;
nsAutoString tempString;
do
{
textNode->GetData(tempString);
offset = tempString.FindCharInSet(newlineChar);
if (offset == -1) break; // done with this node
// delete the newline
EditTxn *txn;
// note 1: we are not telling edit listeners about these because they don't care
// note 2: we are not wrapping these in a placeholder because we know they already are,
// or, failing that, undo is disabled
res = mEditor->CreateTxnForDeleteText(textNode, offset, 1, (DeleteTextTxn**)&txn);
if (NS_FAILED(res)) return res;
if (!txn) return NS_ERROR_OUT_OF_MEMORY;
res = mEditor->Do(txn);
if (NS_FAILED(res)) return res;
// The transaction system (if any) has taken ownwership of txn
NS_IF_RELEASE(txn);
// insert a break
res = mEditor->CreateBR(textNode, offset, &brNode);
if (NS_FAILED(res)) return res;
} while (1); // break used to exit while loop
}
return res;
}
nsresult
nsTextEditRules::CreateBogusNodeIfNeeded(nsIDOMSelection *aSelection)
{

Просмотреть файл

@ -182,6 +182,9 @@ protected:
const nsString &aValue,
nsIDOMSelection *aSelection);
/** replaces newllines with breaks, if needed. acts on doc portion in aRange */
nsresult ReplaceNewlines(nsIDOMRange *aRange);
/** creates a bogus text node if the document has no editable content */
nsresult CreateBogusNodeIfNeeded(nsIDOMSelection *aSelection);