1999-02-12 20:18:58 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
|
|
*
|
1999-11-06 06:43:54 +03:00
|
|
|
* The contents of this file are subject to the Netscape Public
|
|
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
|
|
* except in compliance with the License. You may obtain a copy of
|
|
|
|
* the License at http://www.mozilla.org/NPL/
|
1999-02-12 20:18:58 +03:00
|
|
|
*
|
1999-11-06 06:43:54 +03:00
|
|
|
* Software distributed under the License is distributed on an "AS
|
|
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
|
|
* implied. See the License for the specific language governing
|
|
|
|
* rights and limitations under the License.
|
1999-02-12 20:18:58 +03:00
|
|
|
*
|
1999-11-06 06:43:54 +03:00
|
|
|
* The Original Code is mozilla.org code.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is Netscape
|
1999-02-12 20:18:58 +03:00
|
|
|
* Communications Corporation. Portions created by Netscape are
|
1999-11-06 06:43:54 +03:00
|
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All
|
|
|
|
* Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
2000-01-11 23:49:15 +03:00
|
|
|
* Pierre Phaneuf <pp@ludusdesign.com>
|
1999-02-12 20:18:58 +03:00
|
|
|
*/
|
|
|
|
|
1999-10-21 09:36:21 +04:00
|
|
|
#include "pratom.h"
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
#include "nsVoidArray.h"
|
1999-02-12 20:18:58 +03:00
|
|
|
|
|
|
|
#include "nsIDOMDocument.h"
|
1999-06-09 05:27:08 +04:00
|
|
|
#include "nsIPref.h"
|
|
|
|
#include "nsILocale.h"
|
1999-05-05 08:05:19 +04:00
|
|
|
#include "nsIEditProperty.h" // to be removed XXX
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-02-12 20:18:58 +03:00
|
|
|
#include "nsIDOMText.h"
|
|
|
|
#include "nsIDOMElement.h"
|
|
|
|
#include "nsIDOMAttr.h"
|
|
|
|
#include "nsIDOMNode.h"
|
1999-09-22 05:23:58 +04:00
|
|
|
#include "nsIDOMComment.h"
|
1999-04-21 22:53:55 +04:00
|
|
|
#include "nsIDOMNamedNodeMap.h"
|
1999-02-12 20:18:58 +03:00
|
|
|
#include "nsIDOMNodeList.h"
|
|
|
|
#include "nsIDOMRange.h"
|
|
|
|
#include "nsIDocument.h"
|
1999-05-13 02:24:47 +04:00
|
|
|
#include "nsIDiskDocument.h"
|
1999-02-12 20:18:58 +03:00
|
|
|
#include "nsIServiceManager.h"
|
|
|
|
#include "nsTransactionManagerCID.h"
|
|
|
|
#include "nsITransactionManager.h"
|
1999-09-30 00:08:15 +04:00
|
|
|
#include "nsIAbsorbingTransaction.h"
|
1999-02-12 20:18:58 +03:00
|
|
|
#include "nsIPresShell.h"
|
1999-06-21 11:49:03 +04:00
|
|
|
#include "nsIPresContext.h"
|
1999-02-12 20:18:58 +03:00
|
|
|
#include "nsIViewManager.h"
|
|
|
|
#include "nsIDOMSelection.h"
|
|
|
|
#include "nsIEnumerator.h"
|
|
|
|
#include "nsIAtom.h"
|
1999-05-05 08:05:19 +04:00
|
|
|
#include "nsISupportsArray.h"
|
1999-02-13 07:48:09 +03:00
|
|
|
#include "nsICaret.h"
|
1999-05-17 16:22:31 +04:00
|
|
|
#include "nsIStyleContext.h"
|
1999-04-27 21:14:28 +04:00
|
|
|
#include "nsIEditActionListener.h"
|
1999-12-08 06:39:36 +03:00
|
|
|
#include "nsIKBStateControl.h"
|
|
|
|
#include "nsIWidget.h"
|
|
|
|
#include "nsIScrollbar.h"
|
1999-04-27 21:14:28 +04:00
|
|
|
|
1999-07-01 23:32:35 +04:00
|
|
|
#include "nsICSSLoader.h"
|
|
|
|
#include "nsICSSStyleSheet.h"
|
|
|
|
#include "nsIHTMLContentContainer.h"
|
|
|
|
#include "nsIStyleSet.h"
|
|
|
|
#include "nsIDocumentObserver.h"
|
1999-07-28 06:55:40 +04:00
|
|
|
#include "nsIDocumentStateListener.h"
|
1999-08-09 05:37:50 +04:00
|
|
|
#include "nsIStringStream.h"
|
1999-09-30 00:08:15 +04:00
|
|
|
#include "nsITextContent.h"
|
1999-06-21 11:49:03 +04:00
|
|
|
|
1999-11-30 07:50:42 +03:00
|
|
|
#include "nsNetUtil.h"
|
1999-06-18 21:34:08 +04:00
|
|
|
|
1999-03-10 22:48:13 +03:00
|
|
|
#include "nsIContent.h"
|
|
|
|
#include "nsIContentIterator.h"
|
2000-04-26 05:00:50 +04:00
|
|
|
#include "nsIDocumentEncoder.h"
|
1999-03-10 22:48:13 +03:00
|
|
|
#include "nsLayoutCID.h"
|
1999-02-12 20:18:58 +03:00
|
|
|
|
|
|
|
// transactions the editor knows how to build
|
|
|
|
#include "TransactionFactory.h"
|
|
|
|
#include "EditAggregateTxn.h"
|
1999-09-30 00:08:15 +04:00
|
|
|
#include "PlaceholderTxn.h"
|
1999-02-12 20:18:58 +03:00
|
|
|
#include "ChangeAttributeTxn.h"
|
|
|
|
#include "CreateElementTxn.h"
|
1999-02-24 20:24:37 +03:00
|
|
|
#include "InsertElementTxn.h"
|
1999-02-12 20:18:58 +03:00
|
|
|
#include "DeleteElementTxn.h"
|
|
|
|
#include "InsertTextTxn.h"
|
|
|
|
#include "DeleteTextTxn.h"
|
|
|
|
#include "DeleteRangeTxn.h"
|
|
|
|
#include "SplitElementTxn.h"
|
|
|
|
#include "JoinElementTxn.h"
|
1999-08-09 05:37:50 +04:00
|
|
|
#include "nsStyleSheetTxns.h"
|
1999-06-30 00:31:22 +04:00
|
|
|
#include "IMETextTxn.h"
|
2000-03-13 05:33:19 +03:00
|
|
|
#include "nsIHTMLEditor.h"
|
1999-03-14 03:31:35 +03:00
|
|
|
|
1999-05-27 01:18:12 +04:00
|
|
|
// #define HACK_FORCE_REDRAW 1
|
1999-03-14 03:31:35 +03:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
#include "nsEditorCID.h"
|
|
|
|
#include "nsEditor.h"
|
1999-09-30 00:08:15 +04:00
|
|
|
#include "nsEditorUtils.h"
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-03-14 03:31:35 +03:00
|
|
|
#ifdef HACK_FORCE_REDRAW
|
|
|
|
// INCLUDES FOR EVIL HACK TO FOR REDRAW
|
|
|
|
// BEGIN
|
|
|
|
#include "nsIViewManager.h"
|
|
|
|
#include "nsIView.h"
|
|
|
|
// END
|
As a reminder, we decided to do this based strictly content. Some support for style-based text properties is written, but not used
anywhere any more.
* Cleaned up split and join undo/redo.
* Added TypeInState, a data struct that remembers things about text properties for collapsed selections, so you can type
* Ctrl-B with an insertion point and the next character will be bold.
* Added all the logic to handle inline vs. block elements when setting text properties.
* Added some support for italic and underline as well. Adding these things is pretty easy now. Ctrl-B, Ctrl-I, Ctrl-U for testing bold, italic, underline.
* Added all the logic to make sure we only add style tags where they're needed, so you should never get the same style tag nested within itself, except as needed for block elements.
* Added methods for testing a node to see if a particular style is set. This isn't 100% done yet, but with very little work we could have toolbar buttons that respond to selection changed notification that show the state of bold, italic, underline, etc. in real time. Supports tri-state: whole selection is bold, some of selection is bold, none of selection is bold, ...
* Fully undoable and redoable.
* Added some debug printfs to transactions and editors. all controlled by a gNoisy static in each module. helps me track down undo/redo problems. if the output bugs people enough, I'll shut it off and re-enable it in my local tree.
Noticably missing: make un-bold, make un-italic, etc. This is coming soon.
1999-04-01 21:58:07 +04:00
|
|
|
#endif
|
1999-03-23 18:45:58 +03:00
|
|
|
|
1999-05-05 08:05:19 +04:00
|
|
|
static NS_DEFINE_CID(kCRangeCID, NS_RANGE_CID);
|
1999-06-11 23:01:07 +04:00
|
|
|
static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID);
|
2000-02-10 08:14:52 +03:00
|
|
|
static NS_DEFINE_CID(kCDOMRangeCID, NS_RANGE_CID);
|
2000-04-26 05:00:50 +04:00
|
|
|
static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID);
|
1999-06-21 11:49:03 +04:00
|
|
|
|
1999-02-12 20:18:58 +03:00
|
|
|
// transaction manager
|
1999-03-11 00:27:02 +03:00
|
|
|
static NS_DEFINE_CID(kCTransactionManagerCID, NS_TRANSACTIONMANAGER_CID);
|
1999-05-28 04:20:41 +04:00
|
|
|
|
1999-02-12 20:18:58 +03:00
|
|
|
#ifdef XP_PC
|
|
|
|
#define TRANSACTION_MANAGER_DLL "txmgr.dll"
|
|
|
|
#else
|
|
|
|
#ifdef XP_MAC
|
|
|
|
#define TRANSACTION_MANAGER_DLL "TRANSACTION_MANAGER_DLL"
|
1999-06-25 18:26:21 +04:00
|
|
|
#else // XP_UNIX || XP_BEOS
|
1999-06-05 02:17:30 +04:00
|
|
|
#define TRANSACTION_MANAGER_DLL "libtxmgr"MOZ_DLL_SUFFIX
|
1999-02-12 20:18:58 +03:00
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
1999-03-10 22:48:13 +03:00
|
|
|
#define NS_ERROR_EDITOR_NO_SELECTION NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_EDITOR,1)
|
1999-03-16 19:38:09 +03:00
|
|
|
#define NS_ERROR_EDITOR_NO_TEXTNODE NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_EDITOR,2)
|
|
|
|
|
2000-02-12 04:58:40 +03:00
|
|
|
const char* nsEditor::kMOZEditorBogusNodeAttr="_moz_editor_bogus_node";
|
1999-05-05 08:05:19 +04:00
|
|
|
const char* nsEditor::kMOZEditorBogusNodeValue="TRUE";
|
|
|
|
|
1999-04-13 02:37:20 +04:00
|
|
|
#ifdef NS_DEBUG_EDITOR
|
1999-09-20 08:15:36 +04:00
|
|
|
static PRBool gNoisy = PR_FALSE;
|
As a reminder, we decided to do this based strictly content. Some support for style-based text properties is written, but not used
anywhere any more.
* Cleaned up split and join undo/redo.
* Added TypeInState, a data struct that remembers things about text properties for collapsed selections, so you can type
* Ctrl-B with an insertion point and the next character will be bold.
* Added all the logic to handle inline vs. block elements when setting text properties.
* Added some support for italic and underline as well. Adding these things is pretty easy now. Ctrl-B, Ctrl-I, Ctrl-U for testing bold, italic, underline.
* Added all the logic to make sure we only add style tags where they're needed, so you should never get the same style tag nested within itself, except as needed for block elements.
* Added methods for testing a node to see if a particular style is set. This isn't 100% done yet, but with very little work we could have toolbar buttons that respond to selection changed notification that show the state of bold, italic, underline, etc. in real time. Supports tri-state: whole selection is bold, some of selection is bold, none of selection is bold, ...
* Fully undoable and redoable.
* Added some debug printfs to transactions and editors. all controlled by a gNoisy static in each module. helps me track down undo/redo problems. if the output bugs people enough, I'll shut it off and re-enable it in my local tree.
Noticably missing: make un-bold, make un-italic, etc. This is coming soon.
1999-04-01 21:58:07 +04:00
|
|
|
#else
|
|
|
|
static const PRBool gNoisy = PR_FALSE;
|
|
|
|
#endif
|
|
|
|
|
1999-02-12 20:18:58 +03:00
|
|
|
|
2000-01-15 17:29:29 +03:00
|
|
|
const PRUnichar nbsp = 160;
|
1999-02-12 20:18:58 +03:00
|
|
|
PRInt32 nsEditor::gInstanceCount = 0;
|
|
|
|
|
|
|
|
|
2000-01-31 13:30:12 +03:00
|
|
|
/***************************************************************************
|
|
|
|
* class for recording selection info. stores selection as collection of
|
|
|
|
* { {startnode, startoffset} , {endnode, endoffset} } tuples. Cant store
|
|
|
|
* ranges since dom gravity will possibly change the ranges.
|
|
|
|
*/
|
2000-03-24 03:26:47 +03:00
|
|
|
nsSelectionState::nsSelectionState() : mArray(), mLock(PR_FALSE) {}
|
2000-01-31 13:30:12 +03:00
|
|
|
|
|
|
|
nsSelectionState::~nsSelectionState()
|
|
|
|
{
|
|
|
|
// free any items in the array
|
|
|
|
SelRangeStore *item;
|
2000-03-24 03:26:47 +03:00
|
|
|
while ((item = (SelRangeStore*)mArray.ElementAt(0)))
|
2000-01-31 13:30:12 +03:00
|
|
|
{
|
|
|
|
delete item;
|
|
|
|
mArray.RemoveElementAt(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsSelectionState::SaveSelection(nsIDOMSelection *aSel)
|
|
|
|
{
|
|
|
|
if (!aSel) return NS_ERROR_NULL_POINTER;
|
|
|
|
nsresult res = NS_OK;
|
|
|
|
PRInt32 i,rangeCount, arrayCount = mArray.Count();
|
|
|
|
SelRangeStore *item;
|
|
|
|
aSel->GetRangeCount(&rangeCount);
|
|
|
|
|
|
|
|
// if we need more items in the array, new them
|
|
|
|
if (arrayCount<rangeCount)
|
|
|
|
{
|
|
|
|
PRInt32 count = rangeCount-arrayCount;
|
|
|
|
for (i=0; i<count; i++)
|
|
|
|
{
|
|
|
|
item = new SelRangeStore;
|
|
|
|
mArray.AppendElement(item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// else if we have too many, delete them
|
|
|
|
else if (rangeCount>arrayCount)
|
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
while ((item = (SelRangeStore*)mArray.ElementAt(rangeCount)))
|
2000-01-31 13:30:12 +03:00
|
|
|
{
|
|
|
|
delete item;
|
|
|
|
mArray.RemoveElementAt(rangeCount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// now store the selection ranges
|
|
|
|
for (i=0; i<rangeCount; i++)
|
|
|
|
{
|
|
|
|
item = (SelRangeStore*)mArray.ElementAt(i);
|
|
|
|
if (!item) return NS_ERROR_UNEXPECTED;
|
|
|
|
nsCOMPtr<nsIDOMRange> range;
|
|
|
|
res = aSel->GetRangeAt(i, getter_AddRefs(range));
|
|
|
|
item->StoreRange(range);
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsSelectionState::RestoreSelection(nsIDOMSelection *aSel)
|
|
|
|
{
|
|
|
|
if (!aSel) return NS_ERROR_NULL_POINTER;
|
|
|
|
nsresult res = NS_OK;
|
|
|
|
PRInt32 i, arrayCount = mArray.Count();
|
|
|
|
SelRangeStore *item;
|
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
// clear out selection
|
2000-01-31 13:30:12 +03:00
|
|
|
aSel->ClearSelection();
|
|
|
|
|
|
|
|
// set the selection ranges anew
|
|
|
|
for (i=0; i<arrayCount; i++)
|
|
|
|
{
|
|
|
|
item = (SelRangeStore*)mArray.ElementAt(i);
|
|
|
|
if (!item) return NS_ERROR_UNEXPECTED;
|
|
|
|
nsCOMPtr<nsIDOMRange> range;
|
|
|
|
item->GetRange(&range);
|
|
|
|
if (!range) return NS_ERROR_UNEXPECTED;
|
|
|
|
|
|
|
|
res = aSel->AddRange(range);
|
|
|
|
if(NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
nsSelectionState::IsCollapsed()
|
|
|
|
{
|
|
|
|
if (1 != mArray.Count()) return PR_FALSE;
|
|
|
|
SelRangeStore *item;
|
|
|
|
item = (SelRangeStore*)mArray.ElementAt(0);
|
|
|
|
if (!item) return PR_FALSE;
|
|
|
|
nsCOMPtr<nsIDOMRange> range;
|
|
|
|
item->GetRange(&range);
|
|
|
|
if (!range) return PR_FALSE;
|
|
|
|
PRBool bIsCollapsed;
|
|
|
|
range->GetIsCollapsed(&bIsCollapsed);
|
|
|
|
return bIsCollapsed;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
nsSelectionState::IsEqual(nsSelectionState *aSelState)
|
|
|
|
{
|
|
|
|
if (!aSelState) return NS_ERROR_NULL_POINTER;
|
|
|
|
PRInt32 i, myCount = mArray.Count(), itsCount = aSelState->mArray.Count();
|
|
|
|
if (myCount != itsCount) return PR_FALSE;
|
|
|
|
if (myCount < 1) return PR_FALSE;
|
|
|
|
|
|
|
|
SelRangeStore *myItem, *itsItem;
|
|
|
|
|
|
|
|
for (i=0; i<myCount; i++)
|
|
|
|
{
|
|
|
|
myItem = (SelRangeStore*)mArray.ElementAt(0);
|
|
|
|
itsItem = (SelRangeStore*)(aSelState->mArray.ElementAt(0));
|
|
|
|
if (!myItem || !itsItem) return PR_FALSE;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMRange> myRange, itsRange;
|
|
|
|
myItem->GetRange(&myRange);
|
|
|
|
itsItem->GetRange(&itsRange);
|
|
|
|
if (!myRange || !itsRange) return PR_FALSE;
|
|
|
|
|
|
|
|
PRInt32 compResult;
|
|
|
|
myRange->CompareEndPoints(nsIDOMRange::START_TO_START, itsRange, &compResult);
|
|
|
|
if (compResult) return PR_FALSE;
|
|
|
|
myRange->CompareEndPoints(nsIDOMRange::END_TO_END, itsRange, &compResult);
|
|
|
|
if (compResult) return PR_FALSE;
|
|
|
|
}
|
|
|
|
// if we got here, they are equal
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
// notification routines used to update the saved selection state in response to
|
|
|
|
// document editing.
|
2000-01-31 13:30:12 +03:00
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
nsresult
|
|
|
|
nsSelectionState::SelAdjCreateNode(nsIDOMNode *aParent, PRInt32 aPosition)
|
|
|
|
{
|
|
|
|
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
|
|
|
|
if (!aParent) return NS_ERROR_NULL_POINTER;
|
|
|
|
PRInt32 i, count = mArray.Count();
|
|
|
|
if (!count) return NS_OK;
|
|
|
|
|
|
|
|
SelRangeStore *item;
|
|
|
|
|
|
|
|
for (i=0; i<count; i++)
|
|
|
|
{
|
|
|
|
item = (SelRangeStore*)mArray.ElementAt(i);
|
|
|
|
if (!item) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
if ((item->startNode.get() == aParent) && (item->startOffset > aPosition))
|
|
|
|
item->startOffset++;
|
|
|
|
if ((item->endNode.get() == aParent) && (item->endOffset > aPosition))
|
|
|
|
item->endOffset++;
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsSelectionState::SelAdjInsertNode(nsIDOMNode *aParent, PRInt32 aPosition)
|
|
|
|
{
|
|
|
|
return SelAdjCreateNode(aParent, aPosition);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsSelectionState::SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset)
|
|
|
|
{
|
|
|
|
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
|
|
|
|
if (!aNode) return NS_ERROR_NULL_POINTER;
|
|
|
|
PRInt32 i, count = mArray.Count();
|
|
|
|
if (!count) return NS_OK;
|
|
|
|
|
|
|
|
SelRangeStore *item;
|
|
|
|
|
|
|
|
for (i=0; i<count; i++)
|
|
|
|
{
|
|
|
|
item = (SelRangeStore*)mArray.ElementAt(i);
|
|
|
|
if (!item) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
if ((item->startNode.get() == aParent) && (item->startOffset > aOffset))
|
|
|
|
item->startOffset--;
|
|
|
|
if ((item->endNode.get() == aParent) && (item->endOffset > aOffset))
|
|
|
|
item->endOffset--;
|
|
|
|
}
|
|
|
|
// MOOSE: also check inside of aNode, expensive. But in theory, we shouldn't
|
|
|
|
// actually hit this case in the usage i forsee for this.
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsSelectionState::SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode)
|
|
|
|
{
|
|
|
|
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
|
|
|
|
if (!aOldRightNode || !aNewLeftNode) return NS_ERROR_NULL_POINTER;
|
|
|
|
PRInt32 i, count = mArray.Count();
|
|
|
|
if (!count) return NS_OK;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNode> parent;
|
|
|
|
PRInt32 offset;
|
|
|
|
nsresult result = nsEditor::GetNodeLocation(aOldRightNode, &parent, &offset);
|
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
|
|
|
|
// first part is same as inserting aNewLeftnode
|
2000-04-24 15:51:12 +04:00
|
|
|
result = SelAdjInsertNode(parent,offset-1);
|
2000-03-24 03:26:47 +03:00
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
|
|
|
|
// next step is to check for range enpoints inside aOldRightNode
|
|
|
|
SelRangeStore *item;
|
|
|
|
|
|
|
|
for (i=0; i<count; i++)
|
|
|
|
{
|
|
|
|
item = (SelRangeStore*)mArray.ElementAt(i);
|
|
|
|
if (!item) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
if (item->startNode.get() == aOldRightNode)
|
|
|
|
{
|
|
|
|
if (item->startOffset > aOffset)
|
|
|
|
{
|
|
|
|
item->startOffset -= aOffset;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
item->startNode = aNewLeftNode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (item->endNode.get() == aOldRightNode)
|
|
|
|
{
|
|
|
|
if (item->endOffset > aOffset)
|
|
|
|
{
|
|
|
|
item->endOffset -= aOffset;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
item->endNode = aNewLeftNode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsSelectionState::SelAdjJoinNodes(nsIDOMNode *aLeftNode,
|
|
|
|
nsIDOMNode *aRightNode,
|
|
|
|
nsIDOMNode *aParent,
|
|
|
|
PRInt32 aOffset,
|
|
|
|
PRInt32 aOldLeftNodeLength)
|
|
|
|
{
|
|
|
|
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
|
|
|
|
if (!aLeftNode || !aRightNode || !aParent) return NS_ERROR_NULL_POINTER;
|
|
|
|
PRInt32 i, count = mArray.Count();
|
|
|
|
if (!count) return NS_OK;
|
|
|
|
|
|
|
|
SelRangeStore *item;
|
|
|
|
|
|
|
|
for (i=0; i<count; i++)
|
|
|
|
{
|
|
|
|
item = (SelRangeStore*)mArray.ElementAt(i);
|
|
|
|
if (!item) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
// adjust endpoints in aParent
|
|
|
|
if (item->startNode.get() == aParent)
|
|
|
|
{
|
|
|
|
if (item->startOffset > aOffset)
|
|
|
|
{
|
|
|
|
item->startOffset--;
|
|
|
|
}
|
|
|
|
else if (item->startOffset == aOffset)
|
|
|
|
{
|
|
|
|
// join keeps right hand node
|
|
|
|
item->startNode = aRightNode;
|
|
|
|
item->startOffset = aOldLeftNodeLength;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (item->endNode.get() == aParent)
|
|
|
|
{
|
|
|
|
if (item->endOffset > aOffset)
|
|
|
|
{
|
|
|
|
item->endOffset--;
|
|
|
|
}
|
|
|
|
else if (item->endOffset == aOffset)
|
|
|
|
{
|
|
|
|
// join keeps right hand node
|
|
|
|
item->endNode = aRightNode;
|
|
|
|
item->endOffset = aOldLeftNodeLength;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// adjust endpoints in aRightNode
|
|
|
|
if (item->startNode.get() == aRightNode)
|
|
|
|
item->startOffset += aOldLeftNodeLength;
|
|
|
|
if (item->endNode.get() == aRightNode)
|
|
|
|
item->endOffset += aOldLeftNodeLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsSelectionState::SelAdjInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString)
|
|
|
|
{
|
|
|
|
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsSelectionState::SelAdjDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength)
|
|
|
|
{
|
|
|
|
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsSelectionState::WillReplaceContainer()
|
|
|
|
{
|
|
|
|
if (mLock) return NS_ERROR_UNEXPECTED;
|
|
|
|
mLock = PR_TRUE;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsSelectionState::DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode)
|
|
|
|
{
|
|
|
|
if (!mLock) return NS_ERROR_UNEXPECTED;
|
|
|
|
mLock = PR_FALSE;
|
|
|
|
|
|
|
|
if (!aOriginalNode || !aNewNode) return NS_ERROR_NULL_POINTER;
|
|
|
|
PRInt32 i, count = mArray.Count();
|
|
|
|
if (!count) return NS_OK;
|
|
|
|
|
|
|
|
SelRangeStore *item;
|
|
|
|
|
|
|
|
for (i=0; i<count; i++)
|
|
|
|
{
|
|
|
|
item = (SelRangeStore*)mArray.ElementAt(i);
|
|
|
|
if (!item) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
if (item->startNode.get() == aOriginalNode)
|
|
|
|
item->startNode = aNewNode;
|
|
|
|
if (item->endNode.get() == aOriginalNode)
|
|
|
|
item->endNode = aNewNode;
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsSelectionState::WillRemoveContainer()
|
|
|
|
{
|
|
|
|
if (mLock) return NS_ERROR_UNEXPECTED;
|
|
|
|
mLock = PR_TRUE;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsSelectionState::DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset, PRUint32 aNodeOrigLen)
|
|
|
|
{
|
|
|
|
if (!mLock) return NS_ERROR_UNEXPECTED;
|
|
|
|
mLock = PR_FALSE;
|
|
|
|
|
|
|
|
if (!aNode || !aParent) return NS_ERROR_NULL_POINTER;
|
|
|
|
PRInt32 i, count = mArray.Count();
|
|
|
|
if (!count) return NS_OK;
|
|
|
|
|
|
|
|
SelRangeStore *item;
|
|
|
|
|
|
|
|
for (i=0; i<count; i++)
|
|
|
|
{
|
|
|
|
item = (SelRangeStore*)mArray.ElementAt(i);
|
|
|
|
if (!item) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
if (item->startNode.get() == aNode)
|
|
|
|
{
|
|
|
|
item->startNode = aParent;
|
|
|
|
item->startOffset += aOffset;
|
|
|
|
}
|
|
|
|
if (item->endNode.get() == aNode)
|
|
|
|
{
|
|
|
|
item->endNode = aParent;
|
|
|
|
item->endOffset += aOffset;
|
|
|
|
}
|
|
|
|
if ((item->startNode.get() == aParent) && (item->startOffset > aOffset))
|
|
|
|
item->startOffset += (PRInt32)aNodeOrigLen-1;
|
|
|
|
if ((item->endNode.get() == aParent) && (item->endOffset > aOffset))
|
|
|
|
item->endOffset += (PRInt32)aNodeOrigLen-1;
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsSelectionState::WillInsertContainer()
|
|
|
|
{
|
|
|
|
if (mLock) return NS_ERROR_UNEXPECTED;
|
|
|
|
mLock = PR_TRUE;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsSelectionState::DidInsertContainer()
|
|
|
|
{
|
|
|
|
if (!mLock) return NS_ERROR_UNEXPECTED;
|
|
|
|
mLock = PR_FALSE;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsSelectionState::WillMoveNode()
|
|
|
|
{
|
|
|
|
if (mLock) return NS_ERROR_UNEXPECTED;
|
|
|
|
mLock = PR_TRUE;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsSelectionState::DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOMNode *aNewParent, PRInt32 aNewOffset)
|
|
|
|
{
|
|
|
|
if (!mLock) return NS_ERROR_UNEXPECTED;
|
|
|
|
mLock = PR_FALSE;
|
|
|
|
|
|
|
|
if (!aOldParent || !aNewParent) return NS_ERROR_NULL_POINTER;
|
|
|
|
PRInt32 i, count = mArray.Count();
|
|
|
|
if (!count) return NS_OK;
|
|
|
|
|
|
|
|
SelRangeStore *item;
|
|
|
|
|
|
|
|
for (i=0; i<count; i++)
|
|
|
|
{
|
|
|
|
item = (SelRangeStore*)mArray.ElementAt(i);
|
|
|
|
if (!item) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
// like a delete in aOldParent
|
|
|
|
if ((item->startNode.get() == aOldParent) && (item->startOffset > aOldOffset))
|
|
|
|
item->startOffset--;
|
|
|
|
if ((item->endNode.get() == aOldParent) && (item->endOffset > aOldOffset))
|
|
|
|
item->endOffset--;
|
|
|
|
|
|
|
|
// and like an insert in aNewParent
|
|
|
|
if ((item->startNode.get() == aNewParent) && (item->startOffset > aNewOffset))
|
|
|
|
item->startOffset++;
|
|
|
|
if ((item->endNode.get() == aNewParent) && (item->endOffset > aNewOffset))
|
|
|
|
item->endOffset++;
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
* helper class for nsSelectionState. SelRangeStore stores range endpoints.
|
|
|
|
*/
|
2000-01-31 13:30:12 +03:00
|
|
|
|
|
|
|
nsresult SelRangeStore::StoreRange(nsIDOMRange *aRange)
|
|
|
|
{
|
|
|
|
if (!aRange) return NS_ERROR_NULL_POINTER;
|
|
|
|
aRange->GetStartParent(getter_AddRefs(startNode));
|
|
|
|
aRange->GetEndParent(getter_AddRefs(endNode));
|
|
|
|
aRange->GetStartOffset(&startOffset);
|
|
|
|
aRange->GetEndOffset(&endOffset);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult SelRangeStore::GetRange(nsCOMPtr<nsIDOMRange> *outRange)
|
|
|
|
{
|
|
|
|
if (!outRange) return NS_ERROR_NULL_POINTER;
|
|
|
|
nsresult res = nsComponentManager::CreateInstance(kCRangeCID,
|
|
|
|
nsnull,
|
|
|
|
NS_GET_IID(nsIDOMRange),
|
|
|
|
getter_AddRefs(*outRange));
|
|
|
|
if(NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
res = (*outRange)->SetStart(startNode, startOffset);
|
|
|
|
if(NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
res = (*outRange)->SetEnd(endNode, endOffset);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
/***************************************************************************
|
|
|
|
* another helper class for nsSelectionState. stack based class for doing
|
|
|
|
* Will/DidReplaceContainer()
|
|
|
|
*/
|
|
|
|
|
|
|
|
class nsAutoReplaceContainerSelNotify
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
nsSelectionState *mSel;
|
|
|
|
nsIDOMNode *mOriginalNode;
|
|
|
|
nsIDOMNode *mNewNode;
|
|
|
|
|
|
|
|
public:
|
|
|
|
nsAutoReplaceContainerSelNotify(nsSelectionState *aSelState, nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode) :
|
|
|
|
mSel(aSelState)
|
|
|
|
,mOriginalNode(aOriginalNode)
|
|
|
|
,mNewNode(aNewNode)
|
|
|
|
{
|
|
|
|
if (mSel) mSel->WillReplaceContainer();
|
|
|
|
}
|
|
|
|
|
|
|
|
~nsAutoReplaceContainerSelNotify()
|
|
|
|
{
|
|
|
|
if (mSel) mSel->DidReplaceContainer(mOriginalNode, mNewNode);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
* another helper class for nsSelectionState. stack based class for doing
|
|
|
|
* Will/DidRemoveContainer()
|
|
|
|
*/
|
|
|
|
|
|
|
|
class nsAutoRemoveContainerSelNotify
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
nsSelectionState *mSel;
|
|
|
|
nsIDOMNode *mNode;
|
|
|
|
nsIDOMNode *mParent;
|
|
|
|
PRInt32 mOffset;
|
|
|
|
PRUint32 mNodeOrigLen;
|
|
|
|
|
|
|
|
public:
|
|
|
|
nsAutoRemoveContainerSelNotify(nsSelectionState *aSelState,
|
|
|
|
nsIDOMNode *aNode,
|
|
|
|
nsIDOMNode *aParent,
|
|
|
|
PRInt32 aOffset,
|
|
|
|
PRUint32 aNodeOrigLen) :
|
|
|
|
mSel(aSelState)
|
|
|
|
,mNode(aNode)
|
|
|
|
,mParent(aParent)
|
|
|
|
,mOffset(aOffset)
|
|
|
|
,mNodeOrigLen(aNodeOrigLen)
|
|
|
|
{
|
|
|
|
if (mSel) mSel->WillRemoveContainer();
|
|
|
|
}
|
|
|
|
|
|
|
|
~nsAutoRemoveContainerSelNotify()
|
|
|
|
{
|
|
|
|
if (mSel) mSel->DidRemoveContainer(mNode, mParent, mOffset, mNodeOrigLen);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
* another helper class for nsSelectionState. stack based class for doing
|
|
|
|
* Will/DidInsertContainer()
|
|
|
|
*/
|
|
|
|
|
|
|
|
class nsAutoInsertContainerSelNotify
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
nsSelectionState *mSel;
|
|
|
|
|
|
|
|
public:
|
|
|
|
nsAutoInsertContainerSelNotify(nsSelectionState *aSelState) :
|
|
|
|
mSel(aSelState)
|
|
|
|
{
|
|
|
|
if (mSel) mSel->WillInsertContainer();
|
|
|
|
}
|
|
|
|
|
|
|
|
~nsAutoInsertContainerSelNotify()
|
|
|
|
{
|
|
|
|
if (mSel) mSel->DidInsertContainer();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
* another helper class for nsSelectionState. stack based class for doing
|
|
|
|
* Will/DidMoveNode()
|
|
|
|
*/
|
|
|
|
|
|
|
|
class nsAutoMoveNodeSelNotify
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
nsSelectionState *mSel;
|
|
|
|
nsIDOMNode *mOldParent;
|
|
|
|
nsIDOMNode *mNewParent;
|
|
|
|
PRInt32 mOldOffset;
|
|
|
|
PRInt32 mNewOffset;
|
|
|
|
|
|
|
|
public:
|
|
|
|
nsAutoMoveNodeSelNotify(nsSelectionState *aSelState,
|
|
|
|
nsIDOMNode *aOldParent,
|
|
|
|
PRInt32 aOldOffset,
|
|
|
|
nsIDOMNode *aNewParent,
|
|
|
|
PRInt32 aNewOffset) :
|
|
|
|
mSel(aSelState)
|
|
|
|
,mOldParent(aOldParent)
|
|
|
|
,mNewParent(aNewParent)
|
2000-03-25 05:27:57 +03:00
|
|
|
,mOldOffset(aOldOffset)
|
2000-03-24 03:26:47 +03:00
|
|
|
,mNewOffset(aNewOffset)
|
|
|
|
{
|
|
|
|
if (mSel) mSel->WillMoveNode();
|
|
|
|
}
|
|
|
|
|
|
|
|
~nsAutoMoveNodeSelNotify()
|
|
|
|
{
|
|
|
|
if (mSel) mSel->DidMoveNode(mOldParent, mOldOffset, mNewParent, mNewOffset);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// nsEditor: base editor class implementation
|
|
|
|
//
|
|
|
|
//---------------------------------------------------------------------------
|
2000-01-31 13:30:12 +03:00
|
|
|
|
1999-02-12 20:18:58 +03:00
|
|
|
|
|
|
|
nsEditor::nsEditor()
|
1999-08-25 14:51:55 +04:00
|
|
|
: mPresShellWeak(nsnull)
|
1999-05-28 04:20:41 +04:00
|
|
|
, mViewManager(nsnull)
|
|
|
|
, mUpdateCount(0)
|
1999-09-30 00:08:15 +04:00
|
|
|
, mPlaceHolderTxn(nsnull)
|
|
|
|
, mPlaceHolderName(nsnull)
|
|
|
|
, mPlaceHolderBatch(0)
|
2000-01-31 13:30:12 +03:00
|
|
|
, mSelState(nsnull)
|
2000-03-24 03:26:47 +03:00
|
|
|
, mSavedSel(nsnull)
|
2000-01-04 07:24:04 +03:00
|
|
|
, mShouldTxnSetSelection(PR_TRUE)
|
2000-01-10 13:13:58 +03:00
|
|
|
, mBodyElement(nsnull)
|
1999-10-26 22:54:47 +04:00
|
|
|
, mInIMEMode(PR_FALSE)
|
|
|
|
, mIMETextRangeList(nsnull)
|
|
|
|
, mIMETextNode(nsnull)
|
|
|
|
, mIMETextOffset(0)
|
|
|
|
, mIMEBufferLength(0)
|
1999-10-08 18:39:20 +04:00
|
|
|
, mActionListeners(nsnull)
|
|
|
|
, mDocDirtyState(-1)
|
|
|
|
, mDocWeak(nsnull)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
|
|
|
//initialize member variables here
|
|
|
|
NS_INIT_REFCNT();
|
1999-10-21 09:36:21 +04:00
|
|
|
|
|
|
|
PR_AtomicIncrement(&gInstanceCount);
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
nsEditor::~nsEditor()
|
|
|
|
{
|
1999-08-03 04:58:38 +04:00
|
|
|
// not sure if this needs to be called earlier.
|
|
|
|
NotifyDocumentListeners(eDocumentToBeDestroyed);
|
1999-07-25 22:14:44 +04:00
|
|
|
|
1999-04-27 21:14:28 +04:00
|
|
|
if (mActionListeners)
|
|
|
|
{
|
|
|
|
PRInt32 i;
|
|
|
|
nsIEditActionListener *listener;
|
|
|
|
|
|
|
|
for (i = 0; i < mActionListeners->Count(); i++)
|
|
|
|
{
|
|
|
|
listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
|
|
|
|
NS_IF_RELEASE(listener);
|
|
|
|
}
|
|
|
|
|
|
|
|
delete mActionListeners;
|
|
|
|
mActionListeners = 0;
|
|
|
|
}
|
1999-10-07 00:27:41 +04:00
|
|
|
|
|
|
|
/* shut down all classes that needed initialization */
|
|
|
|
InsertTextTxn::ClassShutdown();
|
|
|
|
IMETextTxn::ClassShutdown();
|
1999-10-26 22:54:47 +04:00
|
|
|
|
1999-10-21 09:36:21 +04:00
|
|
|
PR_AtomicDecrement(&gInstanceCount);
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-05-05 08:05:19 +04:00
|
|
|
// BEGIN nsEditor core implementation
|
1999-02-12 20:18:58 +03:00
|
|
|
|
|
|
|
NS_IMPL_ADDREF(nsEditor)
|
|
|
|
NS_IMPL_RELEASE(nsEditor)
|
2000-04-27 11:37:12 +04:00
|
|
|
NS_IMPL_QUERY_INTERFACE3(nsEditor, nsIEditor, nsIEditorIMESupport, nsISupportsWeakReference)
|
1999-03-02 10:52:41 +03:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
#ifdef XP_MAC
|
|
|
|
#pragma mark -
|
2000-03-24 03:26:47 +03:00
|
|
|
#pragma mark nsIEditorMethods
|
1999-08-09 05:37:50 +04:00
|
|
|
#pragma mark -
|
|
|
|
#endif
|
1999-03-02 08:30:53 +03:00
|
|
|
|
2000-04-28 10:20:36 +04:00
|
|
|
|
1999-03-02 08:30:53 +03:00
|
|
|
NS_IMETHODIMP
|
2000-05-04 12:33:48 +04:00
|
|
|
nsEditor::Init(nsIDOMDocument *aDoc, nsIPresShell* aPresShell, nsIContent *aRoot, nsISelectionController *aSelCon, PRUint32 aFlags)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
|
|
|
NS_PRECONDITION(nsnull!=aDoc && nsnull!=aPresShell, "bad arg");
|
|
|
|
if ((nsnull==aDoc) || (nsnull==aPresShell))
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
mFlags = aFlags;
|
1999-08-25 14:51:55 +04:00
|
|
|
mDocWeak = getter_AddRefs( NS_GetWeakReference(aDoc) ); // weak reference to doc
|
|
|
|
mPresShellWeak = getter_AddRefs( NS_GetWeakReference(aPresShell) ); // weak reference to pres shell
|
2000-04-28 10:20:36 +04:00
|
|
|
mSelConWeak = getter_AddRefs( NS_GetWeakReference(aSelCon) ); // weak reference to selectioncontroller
|
1999-08-25 14:51:55 +04:00
|
|
|
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
|
|
|
|
if (!ps) return NS_ERROR_NOT_INITIALIZED;
|
1999-06-21 11:49:03 +04:00
|
|
|
|
2000-05-04 12:33:48 +04:00
|
|
|
//set up body element if we are passed one.
|
|
|
|
if (aRoot)
|
|
|
|
mBodyElement = do_QueryInterface(aRoot);
|
|
|
|
|
2000-05-15 09:18:45 +04:00
|
|
|
|
1999-06-13 01:15:14 +04:00
|
|
|
|
1999-07-20 02:49:21 +04:00
|
|
|
// Set up the DTD
|
|
|
|
// XXX - in the long run we want to get this from the document, but there
|
|
|
|
// is no way to do that right now. So we leave it null here and set
|
|
|
|
// up a nav html dtd in nsHTMLEditor::Init
|
|
|
|
|
1999-06-13 01:15:14 +04:00
|
|
|
// Init mEditProperty
|
|
|
|
nsresult result = NS_NewEditProperty(getter_AddRefs(mEditProperty));
|
|
|
|
if (NS_FAILED(result)) { return result; }
|
|
|
|
if (!mEditProperty) {return NS_ERROR_NULL_POINTER;}
|
|
|
|
|
1999-08-25 14:51:55 +04:00
|
|
|
ps->GetViewManager(&mViewManager);
|
1999-08-19 17:30:48 +04:00
|
|
|
if (!mViewManager) {return NS_ERROR_NULL_POINTER;}
|
|
|
|
mViewManager->Release(); //we want a weak link
|
|
|
|
|
1999-02-12 20:18:58 +03:00
|
|
|
mUpdateCount=0;
|
1999-02-15 21:25:30 +03:00
|
|
|
InsertTextTxn::ClassInit();
|
1999-02-12 20:18:58 +03:00
|
|
|
|
1999-06-30 00:31:22 +04:00
|
|
|
/* initalize IME stuff */
|
|
|
|
IMETextTxn::ClassInit();
|
|
|
|
mIMETextNode = do_QueryInterface(nsnull);
|
|
|
|
mIMETextOffset = 0;
|
|
|
|
mIMEBufferLength = 0;
|
|
|
|
|
1999-02-13 07:48:09 +03:00
|
|
|
/* Show the caret */
|
2000-05-11 08:22:32 +04:00
|
|
|
aSelCon->SetCaretReadOnly(PR_FALSE);
|
|
|
|
aSelCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
|
2000-06-23 08:00:45 +04:00
|
|
|
|
|
|
|
aSelCon->SetDisplayNonTextSelection(PR_TRUE);//we want to see all the selection reflected to user
|
1999-08-19 17:30:48 +04:00
|
|
|
|
1999-06-23 01:42:44 +04:00
|
|
|
// Set the selection to the beginning:
|
2000-05-15 09:18:45 +04:00
|
|
|
|
|
|
|
//hack to get around this for now.
|
|
|
|
nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mSelConWeak);
|
|
|
|
if (shell)
|
|
|
|
BeginningOfDocument();
|
1999-06-17 01:02:25 +04:00
|
|
|
|
1999-08-25 14:51:55 +04:00
|
|
|
NS_POSTCONDITION(mDocWeak && mPresShellWeak, "bad state");
|
1999-08-03 10:53:45 +04:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::PostCreate()
|
|
|
|
{
|
1999-12-21 18:27:54 +03:00
|
|
|
// nuke the modification count, so the doc appears unmodified
|
|
|
|
// do this before we notify listeners
|
|
|
|
ResetDocModCount();
|
|
|
|
|
1999-07-28 06:55:40 +04:00
|
|
|
// update the UI with our state
|
1999-08-03 04:58:38 +04:00
|
|
|
NotifyDocumentListeners(eDocumentCreated);
|
|
|
|
NotifyDocumentListeners(eDocumentStateChanged);
|
1999-07-28 06:55:40 +04:00
|
|
|
|
2000-03-10 04:30:07 +03:00
|
|
|
// Call ResetInputState() for initialization
|
|
|
|
ForceCompositionEnd();
|
|
|
|
|
1999-02-12 20:18:58 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::GetDocument(nsIDOMDocument **aDoc)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
if (!aDoc)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
*aDoc = nsnull; // init out param
|
1999-08-25 14:51:55 +04:00
|
|
|
NS_PRECONDITION(mDocWeak, "bad state, mDocWeak weak pointer not initialized");
|
|
|
|
if (!mDocWeak) return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(mDocWeak);
|
|
|
|
if (!doc) return NS_ERROR_NOT_INITIALIZED;
|
2000-04-28 10:20:36 +04:00
|
|
|
NS_ADDREF(*aDoc = doc);
|
|
|
|
return NS_OK;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
1999-02-12 20:18:58 +03:00
|
|
|
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsresult
|
|
|
|
nsEditor::GetPresShell(nsIPresShell **aPS)
|
|
|
|
{
|
|
|
|
if (!aPS)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
*aPS = nsnull; // init out param
|
1999-08-25 14:51:55 +04:00
|
|
|
NS_PRECONDITION(mPresShellWeak, "bad state, null mPresShellWeak");
|
|
|
|
if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
|
|
|
|
if (!ps) return NS_ERROR_NOT_INITIALIZED;
|
2000-04-27 11:37:12 +04:00
|
|
|
NS_ADDREF(*aPS = ps);
|
|
|
|
return NS_OK;
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
|
2000-04-27 11:37:12 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::GetSelectionController(nsISelectionController **aSel)
|
|
|
|
{
|
|
|
|
if (!aSel)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
*aSel = nsnull; // init out param
|
2000-04-28 10:20:36 +04:00
|
|
|
NS_PRECONDITION(mSelConWeak, "bad state, null mSelConWeak");
|
|
|
|
if (!mSelConWeak) return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
nsCOMPtr<nsISelectionController> selCon = do_QueryReferent(mSelConWeak);
|
2000-04-27 11:37:12 +04:00
|
|
|
if (!selCon) return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
NS_ADDREF(*aSel = selCon);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::GetSelection(nsIDOMSelection **aSelection)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
if (!aSelection)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
*aSelection = nsnull;
|
2000-04-28 10:20:36 +04:00
|
|
|
if (!mSelConWeak) return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
nsCOMPtr<nsISelectionController> selcon = do_QueryReferent(mSelConWeak);
|
2000-04-27 11:37:12 +04:00
|
|
|
if (!selcon) return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
nsresult result = selcon->GetSelection(nsISelectionController::SELECTION_NORMAL, aSelection); // does an addref
|
1999-08-09 05:37:50 +04:00
|
|
|
return result;
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
|
|
|
|
1999-03-02 08:30:53 +03:00
|
|
|
NS_IMETHODIMP
|
1999-08-09 05:37:50 +04:00
|
|
|
nsEditor::Do(nsITransaction *aTxn)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
if (gNoisy) { printf("Editor::Do ----------\n"); }
|
1999-10-07 00:27:41 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsresult result = NS_OK;
|
1999-09-30 00:08:15 +04:00
|
|
|
|
|
|
|
if (mPlaceHolderBatch && !mPlaceHolderTxn)
|
|
|
|
{
|
|
|
|
// it's pretty darn amazing how many different types of pointers
|
1999-11-01 18:15:35 +03:00
|
|
|
// this transaction goes through here. I bet this is a record.
|
|
|
|
|
|
|
|
// We start off with an EditTxn since that's what the factory returns.
|
1999-10-08 00:46:50 +04:00
|
|
|
EditTxn *editTxn;
|
|
|
|
result = TransactionFactory::GetNewTransaction(PlaceholderTxn::GetCID(), &editTxn);
|
1999-09-30 00:08:15 +04:00
|
|
|
if (NS_FAILED(result)) { return result; }
|
|
|
|
if (!editTxn) { return NS_ERROR_NULL_POINTER; }
|
1999-11-01 18:15:35 +03:00
|
|
|
|
|
|
|
// Then we QI to an nsIAbsorbingTransaction to get at placeholder functionality
|
|
|
|
nsCOMPtr<nsIAbsorbingTransaction> plcTxn;
|
2000-01-11 23:49:15 +03:00
|
|
|
editTxn->QueryInterface(NS_GET_IID(nsIAbsorbingTransaction), getter_AddRefs(plcTxn));
|
1999-11-01 18:15:35 +03:00
|
|
|
// have to use line above instead of "plcTxn = do_QueryInterface(editTxn);"
|
|
|
|
// due to our broken interface model for transactions.
|
|
|
|
|
1999-09-30 00:08:15 +04:00
|
|
|
// save off weak reference to placeholder txn
|
|
|
|
mPlaceHolderTxn = getter_AddRefs( NS_GetWeakReference(plcTxn) );
|
2000-01-31 13:30:12 +03:00
|
|
|
plcTxn->Init(mPresShellWeak, mPlaceHolderName, mSelState);
|
|
|
|
mSelState = nsnull; // placeholder txn took ownership of this pointer
|
|
|
|
|
1999-11-01 18:15:35 +03:00
|
|
|
// finally we QI to an nsITransaction since that's what Do() expects
|
1999-09-30 00:08:15 +04:00
|
|
|
nsCOMPtr<nsITransaction> theTxn = do_QueryInterface(plcTxn);
|
|
|
|
nsITransaction* txn = theTxn;
|
1999-11-01 18:15:35 +03:00
|
|
|
Do(txn); // we will recurse, but will not hit this case in the nested call
|
|
|
|
|
|
|
|
// The transaction system (if any) has taken ownwership of txn
|
|
|
|
NS_IF_RELEASE(txn);
|
1999-09-30 00:08:15 +04:00
|
|
|
}
|
1999-10-07 00:27:41 +04:00
|
|
|
|
|
|
|
if (aTxn)
|
|
|
|
{
|
|
|
|
// get the selection and start a batch change
|
|
|
|
nsCOMPtr<nsIDOMSelection>selection;
|
|
|
|
result = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(result)) { return result; }
|
|
|
|
if (!selection) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
selection->StartBatchChanges();
|
1999-10-07 00:27:41 +04:00
|
|
|
if (mTxnMgr) {
|
|
|
|
result = mTxnMgr->Do(aTxn);
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
1999-10-07 00:27:41 +04:00
|
|
|
else {
|
|
|
|
result = aTxn->Do();
|
|
|
|
}
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
result = DoAfterDoTransaction(aTxn);
|
|
|
|
}
|
|
|
|
|
|
|
|
selection->EndBatchChanges(); // no need to check result here, don't lose result of operation
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
1999-11-01 18:15:35 +03:00
|
|
|
|
1999-10-07 00:27:41 +04:00
|
|
|
NS_POSTCONDITION((NS_SUCCEEDED(result)), "transaction did not execute properly\n");
|
|
|
|
|
1999-02-12 20:18:58 +03:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::EnableUndo(PRBool aEnable)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsresult result=NS_OK;
|
|
|
|
|
|
|
|
if (PR_TRUE==aEnable)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
if (!mTxnMgr)
|
|
|
|
{
|
|
|
|
result = nsComponentManager::CreateInstance(kCTransactionManagerCID,
|
|
|
|
nsnull,
|
2000-01-11 23:49:15 +03:00
|
|
|
NS_GET_IID(nsITransactionManager), getter_AddRefs(mTxnMgr));
|
1999-08-09 05:37:50 +04:00
|
|
|
if (NS_FAILED(result) || !mTxnMgr) {
|
|
|
|
printf("ERROR: Failed to get TransactionManager instance.\n");
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mTxnMgr->SetMaxTransactionCount(-1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // disable the transaction manager if it is enabled
|
|
|
|
if (mTxnMgr)
|
|
|
|
{
|
|
|
|
mTxnMgr->Clear();
|
|
|
|
mTxnMgr->SetMaxTransactionCount(0);
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-02-12 20:18:58 +03:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
|
2000-02-16 04:36:30 +03:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::GetTransactionManager(nsITransactionManager* *aTxnManager)
|
|
|
|
{
|
|
|
|
NS_ENSURE_ARG_POINTER(aTxnManager);
|
|
|
|
|
|
|
|
*aTxnManager = NULL;
|
|
|
|
if (!mTxnMgr)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
NS_ADDREF(*aTxnManager = mTxnMgr);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-03-02 08:30:53 +03:00
|
|
|
NS_IMETHODIMP
|
1999-08-09 05:37:50 +04:00
|
|
|
nsEditor::Undo(PRUint32 aCount)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
if (gNoisy) { printf("Editor::Undo ----------\n"); }
|
|
|
|
nsresult result = NS_OK;
|
1999-12-10 01:35:17 +03:00
|
|
|
ForceCompositionEnd();
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-12-07 11:30:19 +03:00
|
|
|
nsAutoRules beginRulesSniffing(this, kOpUndo, nsIEditor::eNone);
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
BeginUpdateViewBatch();
|
|
|
|
|
|
|
|
if ((nsITransactionManager *)nsnull!=mTxnMgr.get())
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
PRUint32 i=0;
|
|
|
|
for ( ; i<aCount; i++)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
result = mTxnMgr->Undo();
|
|
|
|
|
|
|
|
if (NS_SUCCEEDED(result))
|
|
|
|
result = DoAfterUndoTransaction();
|
1999-08-25 14:51:55 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
if (NS_FAILED(result))
|
|
|
|
break;
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
EndUpdateViewBatch();
|
|
|
|
|
1999-02-12 20:18:58 +03:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
NS_IMETHODIMP nsEditor::CanUndo(PRBool &aIsEnabled, PRBool &aCanUndo)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
aIsEnabled = ((PRBool)((nsITransactionManager *)0!=mTxnMgr.get()));
|
|
|
|
if (aIsEnabled)
|
|
|
|
{
|
|
|
|
PRInt32 numTxns=0;
|
|
|
|
mTxnMgr->GetNumberOfUndoItems(&numTxns);
|
1999-12-11 00:43:52 +03:00
|
|
|
aCanUndo = ((PRBool)(0!=numTxns));
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
else {
|
|
|
|
aCanUndo = PR_FALSE;
|
|
|
|
}
|
|
|
|
return NS_OK;
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-03-02 08:30:53 +03:00
|
|
|
NS_IMETHODIMP
|
1999-08-09 05:37:50 +04:00
|
|
|
nsEditor::Redo(PRUint32 aCount)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
if (gNoisy) { printf("Editor::Redo ----------\n"); }
|
|
|
|
nsresult result = NS_OK;
|
|
|
|
|
1999-12-07 11:30:19 +03:00
|
|
|
nsAutoRules beginRulesSniffing(this, kOpRedo, nsIEditor::eNone);
|
1999-08-09 05:37:50 +04:00
|
|
|
BeginUpdateViewBatch();
|
|
|
|
|
|
|
|
if ((nsITransactionManager *)nsnull!=mTxnMgr.get())
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
PRUint32 i=0;
|
|
|
|
for ( ; i<aCount; i++)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
result = mTxnMgr->Redo();
|
|
|
|
|
|
|
|
if (NS_SUCCEEDED(result))
|
|
|
|
result = DoAfterRedoTransaction();
|
|
|
|
|
|
|
|
if (NS_FAILED(result))
|
|
|
|
break;
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
EndUpdateViewBatch();
|
|
|
|
|
1999-02-12 20:18:58 +03:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
NS_IMETHODIMP nsEditor::CanRedo(PRBool &aIsEnabled, PRBool &aCanRedo)
|
1999-04-21 22:53:55 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
aIsEnabled = ((PRBool)((nsITransactionManager *)0!=mTxnMgr.get()));
|
|
|
|
if (aIsEnabled)
|
|
|
|
{
|
|
|
|
PRInt32 numTxns=0;
|
|
|
|
mTxnMgr->GetNumberOfRedoItems(&numTxns);
|
1999-12-11 00:43:52 +03:00
|
|
|
aCanRedo = ((PRBool)(0!=numTxns));
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
aCanRedo = PR_FALSE;
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
1999-04-21 22:53:55 +04:00
|
|
|
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::BeginTransaction()
|
|
|
|
{
|
|
|
|
BeginUpdateViewBatch();
|
1999-04-21 22:53:55 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
if ((nsITransactionManager *)nsnull!=mTxnMgr.get())
|
1999-04-21 22:53:55 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
mTxnMgr->BeginBatch();
|
1999-05-27 08:10:04 +04:00
|
|
|
}
|
1999-02-12 20:18:58 +03:00
|
|
|
|
1999-07-15 02:29:39 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-03-02 08:30:53 +03:00
|
|
|
NS_IMETHODIMP
|
1999-08-09 05:37:50 +04:00
|
|
|
nsEditor::EndTransaction()
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-07-07 03:02:12 +04:00
|
|
|
if ((nsITransactionManager *)nsnull!=mTxnMgr.get())
|
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
mTxnMgr->EndBatch();
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
1999-05-13 02:24:47 +04:00
|
|
|
|
1999-07-07 03:02:12 +04:00
|
|
|
EndUpdateViewBatch();
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
return NS_OK;
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
|
|
|
|
1999-09-30 00:08:15 +04:00
|
|
|
|
|
|
|
// These two routines are similar to the above, but do not use
|
|
|
|
// the transaction managers batching feature. Instead we use
|
|
|
|
// a placeholder transaction to wrap up any further transaction
|
|
|
|
// while the batch is open. The advantage of this is that
|
|
|
|
// placeholder transactions can later merge, if needed. Merging
|
|
|
|
// is unavailable between transaction manager batches.
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::BeginPlaceHolderTransaction(nsIAtom *aName)
|
|
|
|
{
|
|
|
|
NS_PRECONDITION(mPlaceHolderBatch >= 0, "negative placeholder batch count!");
|
|
|
|
if (!mPlaceHolderBatch)
|
|
|
|
{
|
|
|
|
// time to turn on the batch
|
|
|
|
BeginUpdateViewBatch();
|
|
|
|
mPlaceHolderTxn = nsnull;
|
|
|
|
mPlaceHolderName = aName;
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
|
|
|
nsresult res = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(res)) return res;
|
2000-01-31 13:30:12 +03:00
|
|
|
mSelState = new nsSelectionState();
|
|
|
|
mSelState->SaveSelection(selection);
|
1999-09-30 00:08:15 +04:00
|
|
|
}
|
|
|
|
mPlaceHolderBatch++;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::EndPlaceHolderTransaction()
|
|
|
|
{
|
|
|
|
NS_PRECONDITION(mPlaceHolderBatch > 0, "zero or negative placeholder batch count when ending batch!");
|
|
|
|
if (mPlaceHolderBatch == 1)
|
|
|
|
{
|
|
|
|
// time to turn off the batch
|
|
|
|
EndUpdateViewBatch();
|
2000-01-31 13:30:12 +03:00
|
|
|
if (mSelState)
|
|
|
|
{
|
|
|
|
// we saved the selection state, but never got to hand it to placeholder
|
|
|
|
// (else we ould have nulled out this pointer), so destroy it to prevent leaks.
|
|
|
|
delete mSelState;
|
|
|
|
mSelState = nsnull;
|
|
|
|
}
|
1999-09-30 00:08:15 +04:00
|
|
|
if (mPlaceHolderTxn) // we might have never made a placeholder if no action took place
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryReferent(mPlaceHolderTxn);
|
|
|
|
if (plcTxn)
|
|
|
|
{
|
|
|
|
plcTxn->EndPlaceHolderBatch();
|
|
|
|
}
|
1999-11-05 03:46:06 +03:00
|
|
|
else
|
1999-09-30 00:08:15 +04:00
|
|
|
{
|
1999-11-05 03:46:06 +03:00
|
|
|
// in the future we will check to make sure undo is off here,
|
|
|
|
// since that is the only known case where the placeholdertxn would disappear on us.
|
|
|
|
// For now just removing the assert.
|
1999-09-30 00:08:15 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mPlaceHolderBatch--;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2000-01-04 06:09:41 +03:00
|
|
|
NS_IMETHODIMP nsEditor::ShouldTxnSetSelection(PRBool *aResult)
|
|
|
|
{
|
|
|
|
if (!aResult) return NS_ERROR_NULL_POINTER;
|
|
|
|
*aResult = mShouldTxnSetSelection;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
// XXX: the rule system should tell us which node to select all on (ie, the root, or the body)
|
|
|
|
NS_IMETHODIMP nsEditor::SelectAll()
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-08-25 14:51:55 +04:00
|
|
|
if (!mDocWeak || !mPresShellWeak) { return NS_ERROR_NOT_INITIALIZED; }
|
1999-12-10 01:35:17 +03:00
|
|
|
ForceCompositionEnd();
|
1999-07-07 03:02:12 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
2000-05-08 08:01:26 +04:00
|
|
|
nsCOMPtr<nsISelectionController> selCon = do_QueryReferent(mSelConWeak);
|
|
|
|
if (!selCon) return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
nsresult result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
|
1999-08-09 05:37:50 +04:00
|
|
|
if (NS_SUCCEEDED(result) && selection)
|
1999-07-07 03:02:12 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
result = SelectEntireDocument(selection);
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP nsEditor::BeginningOfDocument()
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-08-25 14:51:55 +04:00
|
|
|
if (!mDocWeak || !mPresShellWeak) { return NS_ERROR_NOT_INITIALIZED; }
|
1999-06-10 23:41:40 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
1999-08-25 14:51:55 +04:00
|
|
|
if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
|
2000-05-08 08:01:26 +04:00
|
|
|
nsCOMPtr<nsISelectionController> selCon = do_QueryReferent(mSelConWeak);
|
|
|
|
if (!selCon) return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
nsresult result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
|
1999-08-09 05:37:50 +04:00
|
|
|
if (NS_SUCCEEDED(result) && selection)
|
1999-05-17 17:31:56 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMNodeList> nodeList;
|
2000-04-18 11:44:58 +04:00
|
|
|
nsAutoString bodyTag; bodyTag.AssignWithConversion("body");
|
1999-08-25 14:51:55 +04:00
|
|
|
nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(mDocWeak);
|
|
|
|
if (!doc) return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
result = doc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList));
|
1999-08-09 05:37:50 +04:00
|
|
|
if ((NS_SUCCEEDED(result)) && nodeList)
|
|
|
|
{
|
|
|
|
PRUint32 count;
|
|
|
|
nodeList->GetLength(&count);
|
1999-11-11 22:22:30 +03:00
|
|
|
if (1!=count) { return NS_ERROR_UNEXPECTED; }
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> bodyNode;
|
|
|
|
result = nodeList->Item(0, getter_AddRefs(bodyNode));
|
|
|
|
if ((NS_SUCCEEDED(result)) && bodyNode)
|
|
|
|
{
|
|
|
|
// Get the first child of the body node:
|
1999-09-22 05:23:58 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> firstNode;
|
1999-11-17 14:03:25 +03:00
|
|
|
result = GetFirstEditableNode(bodyNode, &firstNode);
|
1999-08-09 05:37:50 +04:00
|
|
|
if (firstNode)
|
|
|
|
{
|
1999-09-28 04:49:56 +04:00
|
|
|
// if firstNode is text, set selection to beginning of the text node
|
1999-11-25 03:16:56 +03:00
|
|
|
if (IsTextNode(firstNode))
|
|
|
|
{
|
1999-09-28 04:49:56 +04:00
|
|
|
result = selection->Collapse(firstNode, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // otherwise, it's a leaf node and we set the selection just in front of it
|
|
|
|
nsCOMPtr<nsIDOMNode> parentNode;
|
|
|
|
result = firstNode->GetParentNode(getter_AddRefs(parentNode));
|
|
|
|
if (NS_FAILED(result)) { return result; }
|
|
|
|
if (!parentNode) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
PRInt32 offsetInParent;
|
|
|
|
result = nsEditor::GetChildOffset(firstNode, parentNode, offsetInParent);
|
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
result = selection->Collapse(parentNode, offsetInParent);
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
ScrollIntoView(PR_TRUE);
|
|
|
|
}
|
1999-11-25 03:16:56 +03:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// just the body node, set selection to inside the body
|
|
|
|
result = selection->Collapse(bodyNode, 0);
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
}
|
1999-05-17 17:31:56 +04:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
return result;
|
1999-03-10 22:48:13 +03:00
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP nsEditor::EndOfDocument()
|
1999-03-10 22:48:13 +03:00
|
|
|
{
|
1999-08-25 14:51:55 +04:00
|
|
|
if (!mDocWeak || !mPresShellWeak) { return NS_ERROR_NOT_INITIALIZED; }
|
1999-06-11 01:31:42 +04:00
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
1999-08-25 14:51:55 +04:00
|
|
|
if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
|
2000-05-08 08:01:26 +04:00
|
|
|
nsCOMPtr<nsISelectionController> selCon = do_QueryReferent(mSelConWeak);
|
|
|
|
if (!selCon) return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
nsresult result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
|
1999-06-11 01:31:42 +04:00
|
|
|
if (NS_SUCCEEDED(result) && selection)
|
|
|
|
{
|
1999-06-17 01:02:25 +04:00
|
|
|
nsCOMPtr<nsIDOMNodeList> nodeList;
|
2000-04-18 11:44:58 +04:00
|
|
|
nsAutoString bodyTag; bodyTag.AssignWithConversion("body");
|
1999-08-25 14:51:55 +04:00
|
|
|
nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(mDocWeak);
|
|
|
|
if (!doc) return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
result = doc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList));
|
1999-06-11 01:31:42 +04:00
|
|
|
if ((NS_SUCCEEDED(result)) && nodeList)
|
|
|
|
{
|
|
|
|
PRUint32 count;
|
|
|
|
nodeList->GetLength(&count);
|
1999-09-18 00:22:13 +04:00
|
|
|
NS_VERIFY(PRBool(1==count), "there is not exactly 1 body in the document!");
|
1999-06-17 01:02:25 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> bodyNode;
|
1999-06-11 01:31:42 +04:00
|
|
|
result = nodeList->Item(0, getter_AddRefs(bodyNode));
|
|
|
|
if ((NS_SUCCEEDED(result)) && bodyNode)
|
|
|
|
{
|
1999-11-17 14:03:25 +03:00
|
|
|
nsCOMPtr<nsIDOMNode> lastChild;
|
|
|
|
result = GetLastEditableNode(bodyNode, &lastChild);
|
1999-03-10 22:48:13 +03:00
|
|
|
if ((NS_SUCCEEDED(result)) && lastChild)
|
|
|
|
{
|
1999-06-23 01:42:44 +04:00
|
|
|
// See if the last child is a text node; if so, set offset:
|
|
|
|
PRUint32 offset = 0;
|
|
|
|
if (IsTextNode(lastChild))
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMText> text (do_QueryInterface(lastChild));
|
|
|
|
if (text)
|
|
|
|
text->GetLength(&offset);
|
|
|
|
}
|
1999-07-18 06:27:19 +04:00
|
|
|
result = selection->Collapse(lastChild, offset);
|
1999-06-11 01:31:42 +04:00
|
|
|
ScrollIntoView(PR_FALSE);
|
1999-02-21 00:29:27 +03:00
|
|
|
}
|
1999-11-25 03:16:56 +03:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// just the body node, set selection to inside the body
|
|
|
|
result = selection->Collapse(bodyNode, 0);
|
|
|
|
}
|
1999-02-21 00:29:27 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1999-02-19 02:01:06 +03:00
|
|
|
return result;
|
1999-03-10 22:48:13 +03:00
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::GetDocumentModified(PRBool *outDocModified)
|
1999-03-11 00:29:41 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
if (!outDocModified)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMDocument> theDoc;
|
|
|
|
nsresult rv = GetDocument(getter_AddRefs(theDoc));
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDiskDocument> diskDoc = do_QueryInterface(theDoc, &rv);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
1999-05-14 23:24:10 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
PRInt32 modCount = 0;
|
|
|
|
diskDoc->GetModCount(&modCount);
|
1999-05-14 23:24:10 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
*outDocModified = (modCount != 0);
|
|
|
|
return NS_OK;
|
1999-03-11 00:29:41 +03:00
|
|
|
}
|
|
|
|
|
1999-08-14 02:26:50 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::GetDocumentCharacterSet(PRUnichar** characterSet)
|
|
|
|
{
|
1999-08-21 03:52:36 +04:00
|
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsIDocument> doc;
|
|
|
|
nsCOMPtr<nsIPresShell> presShell;
|
1999-09-15 03:44:05 +04:00
|
|
|
nsAutoString character_set;
|
1999-08-21 03:52:36 +04:00
|
|
|
|
|
|
|
if (characterSet==nsnull) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
rv = GetPresShell(getter_AddRefs(presShell));
|
|
|
|
if (NS_SUCCEEDED(rv))
|
|
|
|
{
|
|
|
|
presShell->GetDocument(getter_AddRefs(doc));
|
|
|
|
if (doc ) {
|
1999-08-25 14:51:55 +04:00
|
|
|
rv = doc->GetDocumentCharacterSet(character_set);
|
|
|
|
if (NS_SUCCEEDED(rv)) *characterSet=character_set.ToNewUnicode();
|
|
|
|
return rv;
|
|
|
|
}
|
1999-08-21 03:52:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
|
1999-08-14 02:26:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::SetDocumentCharacterSet(const PRUnichar* characterSet)
|
|
|
|
{
|
1999-08-21 03:52:36 +04:00
|
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsIDocument> doc;
|
|
|
|
nsCOMPtr<nsIPresShell> presShell;
|
1999-09-15 03:44:05 +04:00
|
|
|
nsAutoString character_set = characterSet;
|
1999-08-21 03:52:36 +04:00
|
|
|
|
|
|
|
if (characterSet==nsnull) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
rv = GetPresShell(getter_AddRefs(presShell));
|
|
|
|
if (NS_SUCCEEDED(rv))
|
|
|
|
{
|
|
|
|
presShell->GetDocument(getter_AddRefs(doc));
|
2000-04-26 05:00:50 +04:00
|
|
|
if (doc) {
|
|
|
|
return doc->SetDocumentCharacterSet(character_set);
|
|
|
|
}
|
1999-08-21 03:52:36 +04:00
|
|
|
}
|
1999-08-14 02:26:50 +04:00
|
|
|
|
1999-08-21 03:52:36 +04:00
|
|
|
return rv;
|
1999-08-14 02:26:50 +04:00
|
|
|
}
|
|
|
|
|
1999-08-28 06:40:18 +04:00
|
|
|
NS_IMETHODIMP
|
2000-04-26 05:00:50 +04:00
|
|
|
nsEditor::SaveFile(nsFileSpec *aFileSpec, PRBool aReplaceExisting,
|
|
|
|
PRBool aSaveCopy, const nsString& aFormat)
|
1999-08-28 06:40:18 +04:00
|
|
|
{
|
|
|
|
if (!aFileSpec)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
1999-12-08 06:39:36 +03:00
|
|
|
|
|
|
|
ForceCompositionEnd();
|
1999-08-28 06:40:18 +04:00
|
|
|
|
|
|
|
// get the document
|
|
|
|
nsCOMPtr<nsIDOMDocument> doc;
|
2000-04-26 05:00:50 +04:00
|
|
|
nsresult rv = GetDocument(getter_AddRefs(doc));
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
1999-08-28 06:40:18 +04:00
|
|
|
if (!doc) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDiskDocument> diskDoc = do_QueryInterface(doc);
|
|
|
|
if (!diskDoc)
|
|
|
|
return NS_ERROR_NO_INTERFACE;
|
|
|
|
|
2000-04-26 05:00:50 +04:00
|
|
|
// Should we prettyprint?
|
|
|
|
PRUint32 flags = 0;
|
|
|
|
NS_WITH_SERVICE(nsIPref, prefService, kPrefServiceCID, &rv);
|
|
|
|
if (NS_SUCCEEDED(rv) && prefService)
|
|
|
|
{
|
|
|
|
PRBool prettyprint = PR_FALSE;;
|
|
|
|
rv = prefService->GetBoolPref("editor.prettyprint", &prettyprint);
|
|
|
|
if (NS_SUCCEEDED(rv) && prettyprint)
|
|
|
|
flags |= nsIDocumentEncoder::OutputFormatted;
|
|
|
|
}
|
|
|
|
|
2000-04-18 11:44:58 +04:00
|
|
|
nsAutoString useDocCharset;
|
1999-08-28 06:40:18 +04:00
|
|
|
|
2000-04-26 05:00:50 +04:00
|
|
|
rv = diskDoc->SaveFile(aFileSpec, aReplaceExisting, aSaveCopy,
|
|
|
|
aFormat, useDocCharset, flags);
|
|
|
|
if (NS_SUCCEEDED(rv))
|
1999-08-28 06:40:18 +04:00
|
|
|
DoAfterDocumentSave();
|
|
|
|
|
2000-04-26 05:00:50 +04:00
|
|
|
return rv;
|
1999-08-28 06:40:18 +04:00
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::SetAttribute(nsIDOMElement *aElement, const nsString& aAttribute, const nsString& aValue)
|
1999-03-11 00:29:41 +03:00
|
|
|
{
|
1999-10-08 00:46:50 +04:00
|
|
|
ChangeAttributeTxn *txn;
|
|
|
|
nsresult result = CreateTxnForSetAttribute(aElement, aAttribute, aValue, &txn);
|
1999-08-09 05:37:50 +04:00
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
result = Do(txn);
|
|
|
|
}
|
1999-11-01 18:15:35 +03:00
|
|
|
// The transaction system (if any) has taken ownwership of txn
|
|
|
|
NS_IF_RELEASE(txn);
|
1999-08-09 05:37:50 +04:00
|
|
|
return result;
|
|
|
|
}
|
1999-06-10 23:41:40 +04:00
|
|
|
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::GetAttributeValue(nsIDOMElement *aElement,
|
|
|
|
const nsString& aAttribute,
|
|
|
|
nsString& aResultValue,
|
|
|
|
PRBool& aResultIsSet)
|
|
|
|
{
|
|
|
|
aResultIsSet=PR_FALSE;
|
|
|
|
nsresult result=NS_OK;
|
|
|
|
if (nsnull!=aElement)
|
1999-07-19 23:37:08 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMAttr> attNode;
|
|
|
|
result = aElement->GetAttributeNode(aAttribute, getter_AddRefs(attNode));
|
|
|
|
if ((NS_SUCCEEDED(result)) && attNode)
|
1999-07-19 23:37:08 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
attNode->GetSpecified(&aResultIsSet);
|
|
|
|
attNode->GetValue(aResultValue);
|
1999-04-02 05:34:05 +04:00
|
|
|
}
|
1999-04-01 18:28:13 +04:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
return result;
|
1999-03-11 00:29:41 +03:00
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::RemoveAttribute(nsIDOMElement *aElement, const nsString& aAttribute)
|
1999-05-27 04:08:15 +04:00
|
|
|
{
|
1999-10-08 00:46:50 +04:00
|
|
|
ChangeAttributeTxn *txn;
|
|
|
|
nsresult result = CreateTxnForRemoveAttribute(aElement, aAttribute, &txn);
|
1999-08-09 05:37:50 +04:00
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
result = Do(txn);
|
|
|
|
}
|
1999-11-01 18:15:35 +03:00
|
|
|
// The transaction system (if any) has taken ownwership of txn
|
|
|
|
NS_IF_RELEASE(txn);
|
1999-08-09 05:37:50 +04:00
|
|
|
return result;
|
1999-05-27 04:08:15 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::MarkNodeDirty(nsIDOMNode* aNode)
|
|
|
|
{
|
|
|
|
// mark the node dirty.
|
|
|
|
nsCOMPtr<nsIDOMElement> element (do_QueryInterface(aNode));
|
|
|
|
if (element)
|
2000-04-18 11:44:58 +04:00
|
|
|
element->SetAttribute(NS_ConvertASCIItoUCS2("_moz_dirty"), nsAutoString());
|
2000-03-24 03:26:47 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef XP_MAC
|
|
|
|
#pragma mark -
|
|
|
|
#pragma mark main node manipulation routines
|
|
|
|
#pragma mark -
|
|
|
|
#endif
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP nsEditor::CreateNode(const nsString& aTag,
|
|
|
|
nsIDOMNode * aParent,
|
|
|
|
PRInt32 aPosition,
|
|
|
|
nsIDOMNode ** aNewNode)
|
1999-07-01 23:32:35 +04:00
|
|
|
{
|
1999-11-25 03:16:56 +03:00
|
|
|
PRInt32 i;
|
|
|
|
nsIEditActionListener *listener;
|
|
|
|
|
1999-12-07 11:30:19 +03:00
|
|
|
nsAutoRules beginRulesSniffing(this, kOpCreateNode, nsIEditor::eNext);
|
|
|
|
|
1999-11-25 03:16:56 +03:00
|
|
|
if (mActionListeners)
|
|
|
|
{
|
|
|
|
for (i = 0; i < mActionListeners->Count(); i++)
|
|
|
|
{
|
|
|
|
listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
|
|
|
|
if (listener)
|
|
|
|
listener->WillCreateNode(aTag, aParent, aPosition);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-10-08 00:46:50 +04:00
|
|
|
CreateElementTxn *txn;
|
|
|
|
nsresult result = CreateTxnForCreateElement(aTag, aParent, aPosition, &txn);
|
1999-08-09 05:37:50 +04:00
|
|
|
if (NS_SUCCEEDED(result))
|
|
|
|
{
|
|
|
|
result = Do(txn);
|
|
|
|
if (NS_SUCCEEDED(result))
|
|
|
|
{
|
|
|
|
result = txn->GetNewNode(aNewNode);
|
|
|
|
NS_ASSERTION((NS_SUCCEEDED(result)), "GetNewNode can't fail if txn::Do succeeded.");
|
|
|
|
}
|
|
|
|
}
|
1999-11-01 18:15:35 +03:00
|
|
|
// The transaction system (if any) has taken ownwership of txn
|
|
|
|
NS_IF_RELEASE(txn);
|
1999-11-25 03:16:56 +03:00
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
if (mSavedSel) mSavedSel->SelAdjCreateNode(aParent, aPosition);
|
|
|
|
|
1999-11-25 03:16:56 +03:00
|
|
|
if (mActionListeners)
|
|
|
|
{
|
|
|
|
for (i = 0; i < mActionListeners->Count(); i++)
|
|
|
|
{
|
|
|
|
listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
|
|
|
|
if (listener)
|
|
|
|
listener->DidCreateNode(aTag, *aNewNode, aParent, aPosition, result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
return result;
|
1999-07-02 07:57:50 +04:00
|
|
|
}
|
1999-07-01 23:32:35 +04:00
|
|
|
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP nsEditor::InsertNode(nsIDOMNode * aNode,
|
|
|
|
nsIDOMNode * aParent,
|
|
|
|
PRInt32 aPosition)
|
1999-07-02 07:57:50 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
PRInt32 i;
|
|
|
|
nsIEditActionListener *listener;
|
1999-12-07 11:30:19 +03:00
|
|
|
nsAutoRules beginRulesSniffing(this, kOpInsertNode, nsIEditor::eNext);
|
1999-07-02 07:57:50 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
if (mActionListeners)
|
1999-07-02 07:57:50 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
for (i = 0; i < mActionListeners->Count(); i++)
|
|
|
|
{
|
|
|
|
listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
|
|
|
|
if (listener)
|
|
|
|
listener->WillInsertNode(aNode, aParent, aPosition);
|
|
|
|
}
|
1999-07-02 07:57:50 +04:00
|
|
|
}
|
1999-07-01 23:32:35 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
InsertElementTxn *txn;
|
|
|
|
nsresult result = CreateTxnForInsertElement(aNode, aParent, aPosition, &txn);
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
result = Do(txn);
|
|
|
|
}
|
1999-11-01 18:15:35 +03:00
|
|
|
// The transaction system (if any) has taken ownwership of txn
|
|
|
|
NS_IF_RELEASE(txn);
|
1999-07-01 23:32:35 +04:00
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
if (mSavedSel) mSavedSel->SelAdjInsertNode(aParent, aPosition);
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
if (mActionListeners)
|
|
|
|
{
|
|
|
|
for (i = 0; i < mActionListeners->Count(); i++)
|
|
|
|
{
|
|
|
|
listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
|
|
|
|
if (listener)
|
|
|
|
listener->DidInsertNode(aNode, aParent, aPosition, result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
1999-07-02 07:57:50 +04:00
|
|
|
}
|
1999-07-01 23:32:35 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::SplitNode(nsIDOMNode * aNode,
|
|
|
|
PRInt32 aOffset,
|
|
|
|
nsIDOMNode **aNewLeftNode)
|
1999-07-02 07:57:50 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
PRInt32 i;
|
|
|
|
nsIEditActionListener *listener;
|
1999-12-07 11:30:19 +03:00
|
|
|
nsAutoRules beginRulesSniffing(this, kOpSplitNode, nsIEditor::eNext);
|
1999-07-01 23:32:35 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
if (mActionListeners)
|
1999-07-02 07:57:50 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
for (i = 0; i < mActionListeners->Count(); i++)
|
|
|
|
{
|
|
|
|
listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
|
|
|
|
if (listener)
|
|
|
|
listener->WillSplitNode(aNode, aOffset);
|
|
|
|
}
|
1999-07-01 23:32:35 +04:00
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
SplitElementTxn *txn;
|
|
|
|
nsresult result = CreateTxnForSplitNode(aNode, aOffset, &txn);
|
|
|
|
if (NS_SUCCEEDED(result))
|
|
|
|
{
|
|
|
|
result = Do(txn);
|
|
|
|
if (NS_SUCCEEDED(result))
|
|
|
|
{
|
|
|
|
result = txn->GetNewNode(aNewLeftNode);
|
|
|
|
NS_ASSERTION((NS_SUCCEEDED(result)), "result must succeeded for GetNewNode");
|
|
|
|
}
|
|
|
|
}
|
1999-11-01 18:15:35 +03:00
|
|
|
// The transaction system (if any) has taken ownwership of txn
|
|
|
|
NS_IF_RELEASE(txn);
|
1999-07-01 23:32:35 +04:00
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
if (mSavedSel) mSavedSel->SelAdjSplitNode(aNode, aOffset, *aNewLeftNode);
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
if (mActionListeners)
|
|
|
|
{
|
|
|
|
for (i = 0; i < mActionListeners->Count(); i++)
|
|
|
|
{
|
|
|
|
listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
|
|
|
|
if (listener)
|
2000-03-24 03:26:47 +03:00
|
|
|
{
|
|
|
|
nsIDOMNode *ptr = (aNewLeftNode) ? *aNewLeftNode : 0;
|
|
|
|
listener->DidSplitNode(aNode, aOffset, ptr, result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1999-09-10 22:54:13 +04:00
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
return result;
|
|
|
|
}
|
1999-09-10 22:54:13 +04:00
|
|
|
|
|
|
|
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::JoinNodes(nsIDOMNode * aLeftNode,
|
|
|
|
nsIDOMNode * aRightNode,
|
|
|
|
nsIDOMNode * aParent)
|
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
PRInt32 i, offset;
|
|
|
|
PRUint32 oldLeftNodeLen;
|
1999-08-09 05:37:50 +04:00
|
|
|
nsIEditActionListener *listener;
|
1999-12-07 11:30:19 +03:00
|
|
|
nsAutoRules beginRulesSniffing(this, kOpJoinNode, nsIEditor::ePrevious);
|
1999-07-01 23:32:35 +04:00
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
// remember some values; later used for saved selection updating.
|
|
|
|
// find the offset between the nodes to be joined.
|
|
|
|
nsresult result = GetChildOffset(aRightNode, aParent, offset);
|
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
// find the number of children of the lefthand node
|
|
|
|
result = GetLengthOfDOMNode(aLeftNode, oldLeftNodeLen);
|
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
if (mActionListeners)
|
|
|
|
{
|
|
|
|
for (i = 0; i < mActionListeners->Count(); i++)
|
|
|
|
{
|
|
|
|
listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
|
|
|
|
if (listener)
|
|
|
|
listener->WillJoinNodes(aLeftNode, aRightNode, aParent);
|
|
|
|
}
|
|
|
|
}
|
1999-07-01 23:32:35 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
JoinElementTxn *txn;
|
2000-03-24 03:26:47 +03:00
|
|
|
result = CreateTxnForJoinNode(aLeftNode, aRightNode, &txn);
|
1999-08-09 05:37:50 +04:00
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
result = Do(txn);
|
|
|
|
}
|
1999-07-01 23:32:35 +04:00
|
|
|
|
1999-11-01 18:15:35 +03:00
|
|
|
// The transaction system (if any) has taken ownwership of txn
|
|
|
|
NS_IF_RELEASE(txn);
|
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
if (mSavedSel) mSavedSel->SelAdjJoinNodes(aLeftNode, aRightNode, aParent, offset, (PRInt32)oldLeftNodeLen);
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
if (mActionListeners)
|
|
|
|
{
|
|
|
|
for (i = 0; i < mActionListeners->Count(); i++)
|
|
|
|
{
|
|
|
|
listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
|
|
|
|
if (listener)
|
|
|
|
listener->DidJoinNodes(aLeftNode, aRightNode, aParent, result);
|
|
|
|
}
|
|
|
|
}
|
1999-07-01 23:32:35 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
return result;
|
|
|
|
}
|
1999-07-01 23:32:35 +04:00
|
|
|
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP nsEditor::DeleteNode(nsIDOMNode * aElement)
|
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
PRInt32 i, offset;
|
|
|
|
nsCOMPtr<nsIDOMNode> parent;
|
1999-08-09 05:37:50 +04:00
|
|
|
nsIEditActionListener *listener;
|
1999-12-07 11:30:19 +03:00
|
|
|
nsAutoRules beginRulesSniffing(this, kOpCreateNode, nsIEditor::ePrevious);
|
1999-07-01 23:32:35 +04:00
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
// save node location for selection updating code.
|
|
|
|
nsresult result = GetNodeLocation(aElement, &parent, &offset);
|
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
if (mActionListeners)
|
|
|
|
{
|
|
|
|
for (i = 0; i < mActionListeners->Count(); i++)
|
|
|
|
{
|
|
|
|
listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
|
|
|
|
if (listener)
|
|
|
|
listener->WillDeleteNode(aElement);
|
1999-07-01 23:32:35 +04:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
|
1999-10-08 00:46:50 +04:00
|
|
|
DeleteElementTxn *txn;
|
2000-03-24 03:26:47 +03:00
|
|
|
result = CreateTxnForDeleteElement(aElement, &txn);
|
1999-08-09 05:37:50 +04:00
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
result = Do(txn);
|
|
|
|
}
|
1999-07-01 23:32:35 +04:00
|
|
|
|
1999-11-01 18:15:35 +03:00
|
|
|
// The transaction system (if any) has taken ownwership of txn
|
|
|
|
NS_IF_RELEASE(txn);
|
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
if (mSavedSel) mSavedSel->SelAdjDeleteNode(aElement, parent, offset);
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
if (mActionListeners)
|
|
|
|
{
|
|
|
|
for (i = 0; i < mActionListeners->Count(); i++)
|
|
|
|
{
|
|
|
|
listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
|
|
|
|
if (listener)
|
|
|
|
listener->DidDeleteNode(aElement, result);
|
|
|
|
}
|
1999-07-01 23:32:35 +04:00
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
return result;
|
1999-07-01 23:32:35 +04:00
|
|
|
}
|
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// ReplaceContainer: replace inNode with a new node (outNode) which is contructed
|
|
|
|
// to be of type aNodeType. Put inNodes children into outNode.
|
|
|
|
// Callers responsibility to make sure inNode's children can
|
|
|
|
// go in outNode.
|
|
|
|
nsresult
|
|
|
|
nsEditor::ReplaceContainer(nsIDOMNode *inNode,
|
|
|
|
nsCOMPtr<nsIDOMNode> *outNode,
|
|
|
|
const nsString &aNodeType,
|
|
|
|
const nsString *aAttribute,
|
2000-06-22 09:39:54 +04:00
|
|
|
const nsString *aValue,
|
|
|
|
PRBool aCloneAttributes)
|
2000-03-24 03:26:47 +03:00
|
|
|
{
|
|
|
|
if (!inNode || !outNode)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
nsresult res;
|
|
|
|
nsCOMPtr<nsIDOMNode> parent;
|
|
|
|
PRInt32 offset;
|
|
|
|
res = GetNodeLocation(inNode, &parent, &offset);
|
|
|
|
if (NS_FAILED(res)) return res;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
// get our doc
|
|
|
|
nsCOMPtr<nsIDOMDocument>doc;
|
|
|
|
res = GetDocument(getter_AddRefs(doc));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!doc) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
// create new container
|
|
|
|
nsCOMPtr<nsIDOMElement> elem;
|
2000-06-07 05:18:12 +04:00
|
|
|
nsString qualifiedTag;
|
|
|
|
qualifiedTag.AssignWithConversion("html:");
|
|
|
|
qualifiedTag+=aNodeType;
|
2000-06-15 13:54:41 +04:00
|
|
|
res = doc->CreateElementNS(NS_ConvertASCIItoUCS2("http://www.w3.org/1999/xhtml"), qualifiedTag, getter_AddRefs(elem));
|
2000-03-24 03:26:47 +03:00
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
*outNode = do_QueryInterface(elem);
|
|
|
|
|
|
|
|
// set attribute if needed
|
|
|
|
if (aAttribute && aValue && !aAttribute->IsEmpty())
|
|
|
|
{
|
|
|
|
res = elem->SetAttribute(*aAttribute, *aValue);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
}
|
2000-06-22 09:39:54 +04:00
|
|
|
if (aCloneAttributes)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode>newNode = do_QueryInterface(elem);
|
|
|
|
res = CloneAttributes(newNode, inNode);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
}
|
2000-03-24 03:26:47 +03:00
|
|
|
|
|
|
|
// notify our internal selection state listener
|
|
|
|
nsAutoReplaceContainerSelNotify selStateNotify(mSavedSel, inNode, *outNode);
|
|
|
|
|
2000-06-22 09:39:54 +04:00
|
|
|
// insert new container into tree
|
|
|
|
res = InsertNode( *outNode, parent, offset);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
// We sometimes insert default <br> (like in a table cell),
|
|
|
|
// so save this to delete later
|
|
|
|
nsCOMPtr<nsIDOMNode> newNodeDefaultChild;
|
|
|
|
nsresult resFirstChild = (*outNode)->GetFirstChild(getter_AddRefs(newNodeDefaultChild));
|
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
// move children into new container
|
2000-06-22 09:39:54 +04:00
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNode> child;
|
|
|
|
/*
|
|
|
|
// This doesn't work when used with table cells!
|
2000-03-24 03:26:47 +03:00
|
|
|
PRBool bHasMoreChildren;
|
|
|
|
inNode->HasChildNodes(&bHasMoreChildren);
|
|
|
|
while (bHasMoreChildren)
|
|
|
|
{
|
|
|
|
inNode->GetLastChild(getter_AddRefs(child));
|
|
|
|
res = DeleteNode(child);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
res = InsertNode(child, *outNode, 0);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
inNode->HasChildNodes(&bHasMoreChildren);
|
|
|
|
}
|
2000-06-22 09:39:54 +04:00
|
|
|
*/
|
|
|
|
// Insert at start of the new node
|
|
|
|
PRInt32 insertIndex = 0;
|
|
|
|
res = inNode->GetFirstChild(getter_AddRefs(child));
|
2000-03-24 03:26:47 +03:00
|
|
|
if (NS_FAILED(res)) return res;
|
2000-06-22 09:39:54 +04:00
|
|
|
while (child)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> nextChild;
|
|
|
|
res = child->GetNextSibling(getter_AddRefs(nextChild));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
res = DeleteNode(child);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
res = InsertNode(child, *outNode, insertIndex);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
child = nextChild;
|
|
|
|
insertIndex++;
|
|
|
|
}
|
|
|
|
// Now that we have some real content in the new node,
|
|
|
|
// delete the default first child
|
|
|
|
// (We must do this AFTER moving the other nodes
|
|
|
|
// because rules would reinsert it if we did it first!)
|
|
|
|
if (NS_SUCCEEDED(resFirstChild) && newNodeDefaultChild)
|
|
|
|
DeleteNode(newNodeDefaultChild);
|
2000-03-24 03:26:47 +03:00
|
|
|
|
2000-06-22 09:39:54 +04:00
|
|
|
// delete old container
|
|
|
|
return DeleteNode(inNode);
|
2000-03-24 03:26:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// RemoveContainer: remove inNode, reparenting it's children into their
|
|
|
|
// the parent of inNode
|
|
|
|
//
|
|
|
|
nsresult
|
|
|
|
nsEditor::RemoveContainer(nsIDOMNode *inNode)
|
1999-07-14 22:54:29 +04:00
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
if (!inNode)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
nsresult res;
|
|
|
|
nsCOMPtr<nsIDOMNode> parent;
|
|
|
|
PRInt32 offset;
|
|
|
|
PRUint32 nodeOrigLen;
|
|
|
|
|
|
|
|
res = GetNodeLocation(inNode, &parent, &offset);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
// loop through the child nodes of inNode and promote them
|
|
|
|
// into inNode's parent.
|
|
|
|
PRBool bHasMoreChildren;
|
|
|
|
inNode->HasChildNodes(&bHasMoreChildren);
|
|
|
|
nsCOMPtr<nsIDOMNodeList> nodeList;
|
|
|
|
res = inNode->GetChildNodes(getter_AddRefs(nodeList));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!nodeList) return NS_ERROR_NULL_POINTER;
|
|
|
|
nodeList->GetLength(&nodeOrigLen);
|
|
|
|
|
|
|
|
// notify our internal selection state listener
|
|
|
|
nsAutoRemoveContainerSelNotify selNotify(mSavedSel, inNode, parent, offset, nodeOrigLen);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNode> child;
|
|
|
|
while (bHasMoreChildren)
|
|
|
|
{
|
|
|
|
inNode->GetLastChild(getter_AddRefs(child));
|
|
|
|
res = DeleteNode(child);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
res = InsertNode(child, parent, offset);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
inNode->HasChildNodes(&bHasMoreChildren);
|
|
|
|
}
|
|
|
|
res = DeleteNode(inNode);
|
|
|
|
return res;
|
1999-07-14 22:54:29 +04:00
|
|
|
}
|
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// InsertContainerAbove: insert a new parent for inNode, returned in outNode,
|
|
|
|
// which is contructed to be of type aNodeType. outNode becomes
|
|
|
|
// a child of inNode's earlier parent.
|
|
|
|
// Callers responsibility to make sure inNode's can be child
|
|
|
|
// of outNode, and outNode can be child of old parent.
|
|
|
|
nsresult
|
|
|
|
nsEditor::InsertContainerAbove( nsIDOMNode *inNode,
|
|
|
|
nsCOMPtr<nsIDOMNode> *outNode,
|
|
|
|
const nsString &aNodeType,
|
|
|
|
const nsString *aAttribute,
|
|
|
|
const nsString *aValue)
|
1999-07-14 22:54:29 +04:00
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
if (!inNode || !outNode)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
nsresult res;
|
|
|
|
nsCOMPtr<nsIDOMNode> parent;
|
|
|
|
PRInt32 offset;
|
|
|
|
res = GetNodeLocation(inNode, &parent, &offset);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
// get our doc
|
|
|
|
nsCOMPtr<nsIDOMDocument>doc;
|
|
|
|
res = GetDocument(getter_AddRefs(doc));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!doc) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
// create new container
|
|
|
|
nsCOMPtr<nsIDOMElement> elem;
|
2000-06-07 05:18:12 +04:00
|
|
|
nsString qualifiedTag;
|
|
|
|
qualifiedTag.AssignWithConversion("html:");
|
|
|
|
qualifiedTag+=aNodeType;
|
2000-06-15 13:54:41 +04:00
|
|
|
res = doc->CreateElementNS(NS_ConvertASCIItoUCS2("http://www.w3.org/1999/xhtml"), qualifiedTag, getter_AddRefs(elem));
|
2000-03-24 03:26:47 +03:00
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
*outNode = do_QueryInterface(elem);
|
|
|
|
|
|
|
|
// set attribute if needed
|
|
|
|
if (aAttribute && aValue && !aAttribute->IsEmpty())
|
|
|
|
{
|
|
|
|
res = elem->SetAttribute(*aAttribute, *aValue);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
// notify our internal selection state listener
|
|
|
|
nsAutoInsertContainerSelNotify selNotify(mSavedSel);
|
|
|
|
|
|
|
|
// put inNode in new parent, outNode
|
|
|
|
res = DeleteNode(inNode);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
res = InsertNode(inNode, *outNode, 0);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
// put new parent in doc
|
|
|
|
res = InsertNode(*outNode, parent, offset);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// MoveNode: move aNode to {aParent,aOffset}
|
|
|
|
nsresult
|
|
|
|
nsEditor::MoveNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset)
|
|
|
|
{
|
|
|
|
if (!aNode || !aParent)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
nsresult res;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNode> oldParent;
|
|
|
|
PRInt32 oldOffset;
|
|
|
|
res = GetNodeLocation(aNode, &oldParent, &oldOffset);
|
|
|
|
|
|
|
|
if (aOffset == -1)
|
|
|
|
{
|
|
|
|
PRUint32 unsignedOffset;
|
|
|
|
// magic value meaning "move to end of aParent"
|
|
|
|
res = GetLengthOfDOMNode(aParent, unsignedOffset);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
aOffset = (PRInt32)unsignedOffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
// dont do anything if it's already in right place
|
|
|
|
if ((aParent == oldParent.get()) && (oldOffset == aOffset)) return NS_OK;
|
|
|
|
|
|
|
|
// notify our internal selection state listener
|
|
|
|
nsAutoMoveNodeSelNotify selNotify(mSavedSel, oldParent, oldOffset, aParent, aOffset);
|
|
|
|
|
|
|
|
// need to adjust aOffset if we are moving aNode further along in it's current parent
|
|
|
|
if ((aParent == oldParent.get()) && (oldOffset < aOffset))
|
|
|
|
{
|
|
|
|
aOffset--; // this is because when we delete aNode, it will make the offsets after it off by one
|
|
|
|
}
|
|
|
|
|
|
|
|
// put aNode in new parent
|
|
|
|
res = DeleteNode(aNode);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
res = InsertNode(aNode, aParent, aOffset);
|
|
|
|
return res;
|
1999-07-14 22:54:29 +04:00
|
|
|
}
|
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
#ifdef XP_MAC
|
|
|
|
#pragma mark -
|
|
|
|
#pragma mark action listener maintainance
|
|
|
|
#pragma mark -
|
|
|
|
#endif
|
|
|
|
|
1999-04-27 21:14:28 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::AddEditActionListener(nsIEditActionListener *aListener)
|
|
|
|
{
|
|
|
|
if (!aListener)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
if (!mActionListeners)
|
|
|
|
{
|
|
|
|
mActionListeners = new nsVoidArray();
|
|
|
|
|
|
|
|
if (!mActionListeners)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mActionListeners->AppendElement((void *)aListener))
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
NS_ADDREF(aListener);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-04-27 21:14:28 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::RemoveEditActionListener(nsIEditActionListener *aListener)
|
|
|
|
{
|
|
|
|
if (!aListener || !mActionListeners)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
if (!mActionListeners->RemoveElement((void *)aListener))
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
NS_IF_RELEASE(aListener);
|
|
|
|
|
|
|
|
if (mActionListeners->Count() < 1)
|
|
|
|
{
|
|
|
|
delete mActionListeners;
|
|
|
|
mActionListeners = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-07-28 06:55:40 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::AddDocumentStateListener(nsIDocumentStateListener *aListener)
|
|
|
|
{
|
|
|
|
if (!aListener)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
|
|
|
|
if (!mDocStateListeners)
|
|
|
|
{
|
|
|
|
rv = NS_NewISupportsArray(getter_AddRefs(mDocStateListeners));
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsISupports> iSupports = do_QueryInterface(aListener, &rv);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
1999-08-03 04:58:38 +04:00
|
|
|
|
|
|
|
// is it already in the list?
|
|
|
|
PRInt32 foundIndex;
|
|
|
|
if (NS_SUCCEEDED(mDocStateListeners->GetIndexOf(iSupports, &foundIndex)) && foundIndex != -1)
|
|
|
|
return NS_OK;
|
1999-07-28 06:55:40 +04:00
|
|
|
|
|
|
|
return mDocStateListeners->AppendElement(iSupports);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::RemoveDocumentStateListener(nsIDocumentStateListener *aListener)
|
|
|
|
{
|
|
|
|
if (!aListener || !mDocStateListeners)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsISupports> iSupports = do_QueryInterface(aListener, &rv);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
return mDocStateListeners->RemoveElement(iSupports);
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
#ifdef XP_MAC
|
|
|
|
#pragma mark -
|
|
|
|
#pragma mark misc
|
|
|
|
#pragma mark -
|
|
|
|
#endif
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsEditor::OutputToString(nsString& aOutputString,
|
|
|
|
const nsString& aFormatType,
|
|
|
|
PRUint32 aFlags)
|
|
|
|
{
|
|
|
|
// these should be implemented by derived classes.
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsEditor::OutputToStream(nsIOutputStream* aOutputStream,
|
|
|
|
const nsString& aFormatType,
|
|
|
|
const nsString* aCharsetOverride,
|
|
|
|
PRUint32 aFlags)
|
|
|
|
{
|
|
|
|
// these should be implemented by derived classes.
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
1999-07-28 06:55:40 +04:00
|
|
|
NS_IMETHODIMP
|
1999-08-09 05:37:50 +04:00
|
|
|
nsEditor::DumpContentTree()
|
1999-07-28 06:55:40 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDocument> thedoc;
|
|
|
|
nsCOMPtr<nsIPresShell> presShell;
|
|
|
|
if (NS_SUCCEEDED(GetPresShell(getter_AddRefs(presShell))))
|
1999-07-28 06:55:40 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
presShell->GetDocument(getter_AddRefs(thedoc));
|
|
|
|
if (thedoc) {
|
|
|
|
nsIContent* root = thedoc->GetRootContent();
|
|
|
|
if (nsnull != root) {
|
|
|
|
root->List(stdout);
|
|
|
|
NS_RELEASE(root);
|
1999-08-03 04:58:38 +04:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
1999-07-28 06:55:40 +04:00
|
|
|
}
|
1999-08-03 04:58:38 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
return NS_OK;
|
1999-07-28 06:55:40 +04:00
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-07-28 06:55:40 +04:00
|
|
|
NS_IMETHODIMP
|
1999-08-09 05:37:50 +04:00
|
|
|
nsEditor::DebugDumpContent() const
|
1999-07-28 06:55:40 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIContent>content;
|
|
|
|
nsCOMPtr<nsIDOMNodeList>nodeList;
|
2000-04-18 11:44:58 +04:00
|
|
|
nsAutoString bodyTag; bodyTag.AssignWithConversion("body");
|
1999-08-25 14:51:55 +04:00
|
|
|
nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(mDocWeak);
|
|
|
|
if (!doc) return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
doc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList));
|
1999-08-09 05:37:50 +04:00
|
|
|
if (nodeList)
|
|
|
|
{
|
|
|
|
PRUint32 count;
|
|
|
|
nodeList->GetLength(&count);
|
|
|
|
NS_ASSERTION(1==count, "there is not exactly 1 body in the document!");
|
|
|
|
nsCOMPtr<nsIDOMNode>bodyNode;
|
|
|
|
nodeList->Item(0, getter_AddRefs(bodyNode));
|
|
|
|
if (bodyNode) {
|
|
|
|
content = do_QueryInterface(bodyNode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
content->List();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
1999-07-28 06:55:40 +04:00
|
|
|
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed)
|
|
|
|
{
|
|
|
|
NS_NOTREACHED("This should never get called. Overridden by subclasses");
|
1999-07-28 06:55:40 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
#ifdef XP_MAC
|
|
|
|
#pragma mark -
|
2000-03-24 03:26:47 +03:00
|
|
|
#pragma mark nsIEditorIMESupport
|
1999-08-09 05:37:50 +04:00
|
|
|
#pragma mark -
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//
|
|
|
|
// The BeingComposition method is called from the Editor Composition event listeners.
|
|
|
|
//
|
|
|
|
NS_IMETHODIMP
|
1999-12-07 04:29:00 +03:00
|
|
|
nsEditor::QueryComposition(nsTextEventReply* aReply)
|
1999-03-10 22:48:13 +03:00
|
|
|
{
|
1999-08-25 14:51:55 +04:00
|
|
|
nsresult result;
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
2000-05-04 13:32:40 +04:00
|
|
|
nsCOMPtr<nsISelectionController> selcon = do_QueryReferent(mSelConWeak);
|
|
|
|
if (selcon)
|
|
|
|
selcon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
|
|
|
|
|
1999-08-25 14:51:55 +04:00
|
|
|
nsCOMPtr<nsIDOMCharacterData> nodeAsText;
|
1999-08-31 05:20:32 +04:00
|
|
|
nsCOMPtr<nsICaret> caretP;
|
1999-08-25 14:51:55 +04:00
|
|
|
if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
|
|
|
|
if (!ps) return NS_ERROR_NOT_INITIALIZED;
|
1999-08-31 05:20:32 +04:00
|
|
|
result = ps->GetCaret(getter_AddRefs(caretP));
|
2000-05-04 13:32:40 +04:00
|
|
|
|
1999-08-31 05:20:32 +04:00
|
|
|
if (NS_SUCCEEDED(result) && caretP) {
|
|
|
|
if (aReply) {
|
1999-12-22 10:56:40 +03:00
|
|
|
caretP->GetWindowRelativeCoordinates(
|
|
|
|
aReply->mCursorPosition,
|
2000-05-04 13:32:40 +04:00
|
|
|
aReply->mCursorIsCollapsed, selection);
|
1999-08-31 05:20:32 +04:00
|
|
|
}
|
|
|
|
}
|
1999-08-25 14:51:55 +04:00
|
|
|
return result;
|
1999-02-19 02:01:06 +03:00
|
|
|
}
|
1999-12-07 04:29:00 +03:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::BeginComposition(nsTextEventReply* aReply)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG_tague
|
|
|
|
printf("nsEditor::StartComposition\n");
|
|
|
|
#endif
|
|
|
|
nsresult ret = QueryComposition(aReply);
|
|
|
|
mInIMEMode = PR_TRUE;
|
|
|
|
return ret;
|
|
|
|
}
|
1999-02-19 02:01:06 +03:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::EndComposition(void)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-10-26 22:54:47 +04:00
|
|
|
if (!mInIMEMode) return NS_OK; // nothing to do
|
1999-08-25 14:51:55 +04:00
|
|
|
|
2000-01-07 01:35:04 +03:00
|
|
|
nsresult result = NS_OK;
|
1999-10-26 22:54:47 +04:00
|
|
|
|
|
|
|
// commit the IME transaction..we can get at it via the transaction mgr.
|
|
|
|
// Note that this means IME won't work without an undo stack!
|
|
|
|
if (mTxnMgr)
|
1999-08-25 14:51:55 +04:00
|
|
|
{
|
1999-11-01 18:15:35 +03:00
|
|
|
nsITransaction *txn;
|
|
|
|
result = mTxnMgr->PeekUndoStack(&txn);
|
|
|
|
// PeekUndoStack does not addref
|
1999-10-26 22:54:47 +04:00
|
|
|
nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryInterface(txn);
|
|
|
|
if (plcTxn)
|
|
|
|
{
|
|
|
|
result = plcTxn->Commit();
|
1999-10-18 18:48:41 +04:00
|
|
|
}
|
1999-08-25 14:51:55 +04:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-08-25 14:51:55 +04:00
|
|
|
/* reset the data we need to construct a transaction */
|
|
|
|
mIMETextNode = do_QueryInterface(nsnull);
|
|
|
|
mIMETextOffset = 0;
|
|
|
|
mIMEBufferLength = 0;
|
1999-10-26 22:54:47 +04:00
|
|
|
mInIMEMode = PR_FALSE;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-08-25 14:51:55 +04:00
|
|
|
return result;
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::SetCompositionString(const nsString& aCompositionString, nsIPrivateTextRangeList* aTextRangeList,nsTextEventReply* aReply)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-10-26 22:54:47 +04:00
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
|
|
|
|
2000-01-19 00:50:15 +03:00
|
|
|
static nsIWidget* GetDeepestWidget(nsIView * aView)
|
1999-12-08 06:39:36 +03:00
|
|
|
{
|
|
|
|
|
|
|
|
PRInt32 count;
|
|
|
|
aView->GetChildCount(count);
|
|
|
|
if (0 != count) {
|
|
|
|
for (PRInt32 i=0;i<count;i++) {
|
|
|
|
nsIView * child;
|
|
|
|
aView->GetChild(i, child);
|
|
|
|
nsIWidget * widget = GetDeepestWidget(child);
|
|
|
|
if (widget) {
|
|
|
|
return widget;
|
|
|
|
} else {
|
|
|
|
aView->GetWidget(widget);
|
|
|
|
if (widget) {
|
|
|
|
nsCOMPtr<nsIScrollbar> scrollbar(do_QueryInterface(widget));
|
|
|
|
if (scrollbar) {
|
|
|
|
NS_RELEASE(widget);
|
|
|
|
} else {
|
|
|
|
return widget;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::ForceCompositionEnd()
|
|
|
|
{
|
|
|
|
|
|
|
|
// We can test mInIMEMode and do some optimization for Mac and Window
|
|
|
|
// Howerver, since UNIX support over-the-spot, we cannot rely on that
|
|
|
|
// flag for Unix.
|
|
|
|
// We should use nsILookAndFeel to resolve this
|
|
|
|
|
|
|
|
#if defined(XP_MAC) || defined(XP_WIN)
|
|
|
|
if(! mInIMEMode)
|
|
|
|
return NS_OK;
|
|
|
|
#endif
|
|
|
|
|
2000-03-13 05:33:19 +03:00
|
|
|
#ifdef XP_UNIX
|
|
|
|
if(mFlags & nsIHTMLEditor::eEditorPasswordMask)
|
|
|
|
return NS_OK;
|
|
|
|
#endif
|
|
|
|
|
1999-12-08 06:39:36 +03:00
|
|
|
nsresult res = NS_OK;
|
|
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
|
|
|
|
|
|
GetPresShell(getter_AddRefs(shell));
|
|
|
|
if (shell) {
|
|
|
|
nsCOMPtr<nsIViewManager> viewmgr;
|
|
|
|
|
|
|
|
shell->GetViewManager(getter_AddRefs(viewmgr));
|
|
|
|
if (viewmgr) {
|
|
|
|
nsIView* view;
|
|
|
|
viewmgr->GetRootView(view); // views are not refCounted
|
|
|
|
nsIWidget *widget = GetDeepestWidget(view);
|
|
|
|
if (widget) {
|
|
|
|
nsCOMPtr<nsIKBStateControl> kb = do_QueryInterface(widget);
|
|
|
|
if(kb) {
|
|
|
|
res = kb->ResetInputState();
|
|
|
|
}
|
2000-03-23 04:14:49 +03:00
|
|
|
NS_RELEASE(widget);
|
1999-12-08 06:39:36 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
#ifdef XP_MAC
|
|
|
|
#pragma mark -
|
2000-03-24 03:26:47 +03:00
|
|
|
#pragma mark public nsEditor methods
|
1999-08-09 05:37:50 +04:00
|
|
|
#pragma mark -
|
|
|
|
#endif
|
|
|
|
/* Non-interface, public methods */
|
|
|
|
|
|
|
|
|
|
|
|
// This seems like too much work! There should be a "nsDOMDocument::GetBody()"
|
|
|
|
// Have a look in nsHTMLDocument. Maybe add it to nsIHTMLDocument.
|
|
|
|
NS_IMETHODIMP
|
2000-05-04 12:33:48 +04:00
|
|
|
nsEditor::GetRootElement(nsIDOMElement **aBodyElement)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
2000-01-10 13:13:58 +03:00
|
|
|
nsresult result = NS_OK;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
if (!aBodyElement)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
*aBodyElement = 0;
|
|
|
|
|
2000-01-10 13:13:58 +03:00
|
|
|
if (mBodyElement)
|
|
|
|
{
|
|
|
|
// if we have cached the body element, use that
|
|
|
|
*aBodyElement = mBodyElement;
|
|
|
|
NS_ADDREF(*aBodyElement);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-08-25 14:51:55 +04:00
|
|
|
NS_PRECONDITION(mDocWeak, "bad state, null mDocWeak");
|
|
|
|
if (!mDocWeak)
|
1999-08-09 05:37:50 +04:00
|
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNodeList>nodeList;
|
2000-04-18 11:44:58 +04:00
|
|
|
nsAutoString bodyTag; bodyTag.AssignWithConversion("body");
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-08-25 14:51:55 +04:00
|
|
|
nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(mDocWeak);
|
|
|
|
if (!doc) return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
result = doc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList));
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
if (NS_FAILED(result))
|
|
|
|
return result;
|
|
|
|
|
|
|
|
if (!nodeList)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
PRUint32 count;
|
|
|
|
nodeList->GetLength(&count);
|
|
|
|
|
|
|
|
if (count < 1)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
// Use the first body node in the list:
|
|
|
|
nsCOMPtr<nsIDOMNode> node;
|
|
|
|
result = nodeList->Item(0, getter_AddRefs(node));
|
|
|
|
if (NS_SUCCEEDED(result) && node)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
2000-01-11 23:49:15 +03:00
|
|
|
//return node->QueryInterface(NS_GET_IID(nsIDOMElement), (void **)aBodyElement);
|
1999-08-09 05:37:50 +04:00
|
|
|
// Is above equivalent to this:
|
|
|
|
nsCOMPtr<nsIDOMElement> bodyElement = do_QueryInterface(node);
|
|
|
|
if (bodyElement)
|
|
|
|
{
|
2000-01-10 13:13:58 +03:00
|
|
|
mBodyElement = do_QueryInterface(bodyElement);
|
1999-08-09 05:37:50 +04:00
|
|
|
*aBodyElement = bodyElement;
|
|
|
|
// A "getter" method should always addref
|
|
|
|
NS_ADDREF(*aBodyElement);
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-12-07 11:30:19 +03:00
|
|
|
/** All editor operations which alter the doc should be prefaced
|
|
|
|
* with a call to StartOperation, naming the action and direction */
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection)
|
|
|
|
{
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** All editor operations which alter the doc should be followed
|
|
|
|
* with a call to EndOperation, naming the action and direction */
|
|
|
|
NS_IMETHODIMP
|
2000-03-24 03:26:47 +03:00
|
|
|
nsEditor::EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection)
|
1999-12-07 11:30:19 +03:00
|
|
|
{
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
// Objects must be DOM elements
|
|
|
|
NS_IMETHODIMP
|
1999-09-02 05:47:18 +04:00
|
|
|
nsEditor::CloneAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsresult result=NS_OK;
|
1999-03-02 08:30:53 +03:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
if (!aDestNode || !aSourceNode)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
1999-03-02 08:30:53 +03:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMElement> destElement = do_QueryInterface(aDestNode);
|
|
|
|
nsCOMPtr<nsIDOMElement> sourceElement = do_QueryInterface(aSourceNode);
|
|
|
|
if (!destElement || !sourceElement)
|
|
|
|
return NS_ERROR_NO_INTERFACE;
|
1999-03-02 08:30:53 +03:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsAutoString name;
|
|
|
|
nsAutoString value;
|
|
|
|
nsCOMPtr<nsIDOMNamedNodeMap> sourceAttributes;
|
|
|
|
sourceElement->GetAttributes(getter_AddRefs(sourceAttributes));
|
|
|
|
nsCOMPtr<nsIDOMNamedNodeMap> destAttributes;
|
|
|
|
destElement->GetAttributes(getter_AddRefs(destAttributes));
|
|
|
|
if (!sourceAttributes || !destAttributes)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2000-04-14 07:19:31 +04:00
|
|
|
nsAutoEditBatch beginBatching(this);
|
|
|
|
|
2000-06-14 05:32:27 +04:00
|
|
|
// Use transaction system for undo only if destination
|
|
|
|
// is already in the document
|
|
|
|
PRBool destInBody = PR_TRUE;
|
|
|
|
nsCOMPtr<nsIDOMElement> bodyElement;
|
|
|
|
nsresult res = GetRootElement(getter_AddRefs(bodyElement));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!bodyElement) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNode> bodyNode = do_QueryInterface(bodyElement);
|
|
|
|
nsCOMPtr<nsIDOMNode> p = aDestNode;
|
|
|
|
while (p && p!= bodyNode)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> tmp;
|
|
|
|
if (NS_FAILED(p->GetParentNode(getter_AddRefs(tmp))) || !tmp)
|
|
|
|
{
|
|
|
|
destInBody = PR_FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
p = tmp;
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
PRUint32 sourceCount;
|
|
|
|
sourceAttributes->GetLength(&sourceCount);
|
|
|
|
PRUint32 i, destCount;
|
|
|
|
destAttributes->GetLength(&destCount);
|
|
|
|
nsIDOMNode* attrNode;
|
|
|
|
|
|
|
|
// Clear existing attributes
|
|
|
|
for (i = 0; i < destCount; i++)
|
|
|
|
{
|
1999-11-18 23:34:15 +03:00
|
|
|
// always remove item number 0 (first item in list)
|
|
|
|
if( NS_SUCCEEDED(destAttributes->Item(0, &attrNode)) && attrNode)
|
1999-03-02 08:30:53 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMAttr> destAttribute = do_QueryInterface(attrNode);
|
|
|
|
if (destAttribute)
|
|
|
|
{
|
2000-04-14 07:19:31 +04:00
|
|
|
nsAutoString str;
|
|
|
|
if (NS_SUCCEEDED(destAttribute->GetName(str)))
|
2000-06-14 05:32:27 +04:00
|
|
|
{
|
|
|
|
if (destInBody)
|
|
|
|
RemoveAttribute(destElement, str);
|
|
|
|
else
|
|
|
|
destElement->RemoveAttribute(str);
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Set just the attributes that the source element has
|
|
|
|
for (i = 0; i < sourceCount; i++)
|
|
|
|
{
|
|
|
|
if( NS_SUCCEEDED(sourceAttributes->Item(i, &attrNode)) && attrNode)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMAttr> sourceAttribute = do_QueryInterface(attrNode);
|
|
|
|
if (sourceAttribute)
|
|
|
|
{
|
1999-09-15 03:44:05 +04:00
|
|
|
nsAutoString sourceAttrName;
|
1999-08-09 05:37:50 +04:00
|
|
|
if (NS_SUCCEEDED(sourceAttribute->GetName(sourceAttrName)))
|
|
|
|
{
|
1999-09-15 03:44:05 +04:00
|
|
|
nsAutoString sourceAttrValue;
|
1999-08-09 05:37:50 +04:00
|
|
|
if (NS_SUCCEEDED(sourceAttribute->GetValue(sourceAttrValue)) &&
|
2000-03-26 15:39:08 +04:00
|
|
|
!sourceAttrValue.IsEmpty())
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
2000-06-14 05:32:27 +04:00
|
|
|
if (destInBody)
|
|
|
|
SetAttribute(destElement, sourceAttrName, sourceAttrValue);
|
|
|
|
else
|
|
|
|
destElement->SetAttribute(sourceAttrName, sourceAttrValue);
|
1999-08-09 05:37:50 +04:00
|
|
|
} else {
|
|
|
|
// Do we ever get here?
|
|
|
|
#if DEBUG_cmanske
|
2000-03-21 09:05:24 +03:00
|
|
|
printf("Attribute in sourceAttribute has empty value in nsEditor::CloneAttributes()\n");
|
1999-08-09 05:37:50 +04:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
1999-02-15 21:25:30 +03:00
|
|
|
}
|
|
|
|
}
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
1999-03-02 08:30:53 +03:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-02-12 20:18:58 +03:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
#ifdef XP_MAC
|
|
|
|
#pragma mark -
|
2000-03-24 03:26:47 +03:00
|
|
|
#pragma mark Protected and static methods
|
1999-08-09 05:37:50 +04:00
|
|
|
#pragma mark -
|
|
|
|
#endif
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsEditor::ScrollIntoView(PRBool aScrollToBegin)
|
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
1999-08-31 02:12:11 +04:00
|
|
|
/** static helper method */
|
|
|
|
nsresult nsEditor::GetTextNodeTag(nsString& aOutString)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
2000-04-18 11:44:58 +04:00
|
|
|
aOutString.SetLength(0);
|
1999-08-31 02:12:11 +04:00
|
|
|
static nsString *gTextNodeTag=nsnull;
|
|
|
|
if (!gTextNodeTag)
|
|
|
|
{
|
2000-04-18 11:44:58 +04:00
|
|
|
if ( (gTextNodeTag = new nsString) == 0 )
|
1999-08-31 02:12:11 +04:00
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
2000-04-18 11:44:58 +04:00
|
|
|
gTextNodeTag->AssignWithConversion("special text node tag");
|
1999-08-31 02:12:11 +04:00
|
|
|
}
|
|
|
|
aOutString = *gTextNodeTag;
|
|
|
|
return NS_OK;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-04-14 01:50:19 +04:00
|
|
|
NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert,
|
2000-03-24 03:26:47 +03:00
|
|
|
nsCOMPtr<nsIDOMNode> *aInOutNode,
|
|
|
|
PRInt32 *aInOutOffset,
|
|
|
|
nsIDOMDocument *aDoc)
|
|
|
|
{
|
|
|
|
// NOTE: caller *must* have already used nsAutoTxnsConserveSelection stack-based
|
|
|
|
// class to turn off txn selection updating. Caller also turned on rules sniffing
|
|
|
|
// if desired.
|
|
|
|
|
|
|
|
if (!aInOutNode || !*aInOutNode || !aInOutOffset || !aDoc) return NS_ERROR_NULL_POINTER;
|
2000-04-14 01:50:19 +04:00
|
|
|
if (!mInIMEMode && aStringToInsert.IsEmpty()) return NS_OK;
|
2000-03-24 03:26:47 +03:00
|
|
|
nsCOMPtr<nsIDOMText> nodeAsText = do_QueryInterface(*aInOutNode);
|
|
|
|
PRInt32 offset = *aInOutOffset;
|
|
|
|
nsresult res;
|
2000-03-29 16:53:23 +04:00
|
|
|
if (mInIMEMode)
|
2000-03-24 03:26:47 +03:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
if (!nodeAsText)
|
|
|
|
{
|
|
|
|
// create a text node
|
|
|
|
nsCOMPtr<nsIDOMNode> newNode;
|
2000-04-18 11:44:58 +04:00
|
|
|
res = aDoc->CreateTextNode(nsAutoString(), getter_AddRefs(nodeAsText));
|
2000-03-29 16:53:23 +04:00
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!nodeAsText) return NS_ERROR_NULL_POINTER;
|
|
|
|
newNode = do_QueryInterface(nodeAsText);
|
|
|
|
// then we insert it into the dom tree
|
|
|
|
res = InsertNode(newNode, *aInOutNode, offset);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
offset = 0;
|
|
|
|
}
|
2000-04-14 01:50:19 +04:00
|
|
|
res = InsertTextIntoTextNodeImpl(aStringToInsert, nodeAsText, offset);
|
2000-03-24 03:26:47 +03:00
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
if (nodeAsText)
|
|
|
|
{
|
|
|
|
// we are inserting text into an existing text node.
|
2000-04-14 01:50:19 +04:00
|
|
|
res = InsertTextIntoTextNodeImpl(aStringToInsert, nodeAsText, offset);
|
2000-03-29 16:53:23 +04:00
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
*aInOutOffset += aStringToInsert.Length();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> newNode;
|
|
|
|
// we are inserting text into a non-text node
|
|
|
|
// first we have to create a textnode (this also populates it with the text)
|
|
|
|
res = aDoc->CreateTextNode(aStringToInsert, getter_AddRefs(nodeAsText));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!nodeAsText) return NS_ERROR_NULL_POINTER;
|
|
|
|
newNode = do_QueryInterface(nodeAsText);
|
|
|
|
// then we insert it into the dom tree
|
|
|
|
res = InsertNode(newNode, *aInOutNode, offset);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
*aInOutNode = newNode;
|
|
|
|
*aInOutOffset = aStringToInsert.Length();
|
|
|
|
}
|
2000-03-24 03:26:47 +03:00
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-04-14 01:50:19 +04:00
|
|
|
NS_IMETHODIMP nsEditor::InsertTextIntoTextNodeImpl(const nsString& aStringToInsert,
|
2000-03-24 03:26:47 +03:00
|
|
|
nsIDOMCharacterData *aTextNode,
|
|
|
|
PRInt32 aOffset)
|
|
|
|
{
|
|
|
|
EditTxn *txn;
|
2000-03-29 16:53:23 +04:00
|
|
|
nsresult result;
|
|
|
|
if (mInIMEMode)
|
|
|
|
{
|
|
|
|
if (!mIMETextNode)
|
|
|
|
{
|
|
|
|
mIMETextNode = aTextNode;
|
|
|
|
mIMETextOffset = aOffset;
|
|
|
|
}
|
|
|
|
result = CreateTxnForIMEText(aStringToInsert, (IMETextTxn**)&txn);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result = CreateTxnForInsertText(aStringToInsert, aTextNode, aOffset, (InsertTextTxn**)&txn);
|
|
|
|
}
|
2000-03-24 03:26:47 +03:00
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
|
|
|
|
// let listeners know whats up
|
|
|
|
PRInt32 i;
|
|
|
|
nsIEditActionListener *listener;
|
|
|
|
if (mActionListeners)
|
|
|
|
{
|
|
|
|
for (i = 0; i < mActionListeners->Count(); i++)
|
|
|
|
{
|
|
|
|
listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
|
|
|
|
if (listener)
|
|
|
|
listener->WillInsertText(aTextNode, aOffset, aStringToInsert);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BeginUpdateViewBatch();
|
|
|
|
result = Do(txn);
|
|
|
|
// The transaction system (if any) has taken ownwership of txns.
|
|
|
|
// aggTxn released at end of routine.
|
|
|
|
NS_IF_RELEASE(txn);
|
|
|
|
EndUpdateViewBatch();
|
|
|
|
|
|
|
|
// let listeners know what happened
|
|
|
|
if (mActionListeners)
|
|
|
|
{
|
|
|
|
for (i = 0; i < mActionListeners->Count(); i++)
|
|
|
|
{
|
|
|
|
listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
|
|
|
|
if (listener)
|
|
|
|
listener->DidInsertText(aTextNode, aOffset, aStringToInsert, result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP nsEditor::SelectEntireDocument(nsIDOMSelection *aSelection)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
|
|
|
nsresult result;
|
1999-08-09 05:37:50 +04:00
|
|
|
if (!aSelection) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
nsCOMPtr<nsIDOMElement>bodyElement;
|
2000-05-04 12:33:48 +04:00
|
|
|
result = GetRootElement(getter_AddRefs(bodyElement));
|
1999-08-09 05:37:50 +04:00
|
|
|
if ((NS_SUCCEEDED(result)) && bodyElement)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMNode>bodyNode = do_QueryInterface(bodyElement);
|
|
|
|
if (bodyNode)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
result = aSelection->Collapse(bodyNode, 0);
|
|
|
|
if (NS_SUCCEEDED(result))
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
PRInt32 numBodyChildren=0;
|
|
|
|
nsCOMPtr<nsIDOMNode>lastChild;
|
|
|
|
result = bodyNode->GetLastChild(getter_AddRefs(lastChild));
|
|
|
|
if ((NS_SUCCEEDED(result)) && lastChild)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
GetChildOffset(lastChild, bodyNode, numBodyChildren);
|
|
|
|
result = aSelection->Extend(bodyNode, numBodyChildren+1);
|
1999-03-10 22:48:13 +03:00
|
|
|
}
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
|
|
|
}
|
1999-03-10 22:48:13 +03:00
|
|
|
else {
|
1999-08-09 05:37:50 +04:00
|
|
|
return NS_ERROR_NO_INTERFACE;
|
1999-03-10 22:48:13 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-09-22 05:23:58 +04:00
|
|
|
|
1999-11-17 14:03:25 +03:00
|
|
|
nsresult nsEditor::GetFirstEditableNode(nsIDOMNode *aRoot, nsCOMPtr<nsIDOMNode> *outFirstNode)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
1999-11-17 14:03:25 +03:00
|
|
|
if (!aRoot || !outFirstNode) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
1999-09-22 05:23:58 +04:00
|
|
|
*outFirstNode = nsnull;
|
1999-11-17 14:03:25 +03:00
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNode> node,next;
|
|
|
|
nsresult rv = GetLeftmostChild(aRoot, getter_AddRefs(node));
|
1999-09-22 05:23:58 +04:00
|
|
|
if (NS_FAILED(rv)) return rv;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-11-17 14:03:25 +03:00
|
|
|
if (node && !IsEditable(node))
|
1999-09-22 05:23:58 +04:00
|
|
|
{
|
1999-11-17 14:03:25 +03:00
|
|
|
rv = GetNextNode(node, PR_TRUE, getter_AddRefs(next));
|
|
|
|
node = next;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
1999-09-22 05:23:58 +04:00
|
|
|
|
1999-11-25 03:16:56 +03:00
|
|
|
if (node.get() != aRoot) *outFirstNode = node;
|
1999-11-17 14:03:25 +03:00
|
|
|
|
|
|
|
return rv;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-11-17 14:03:25 +03:00
|
|
|
nsresult nsEditor::GetLastEditableNode(nsIDOMNode *aRoot, nsCOMPtr<nsIDOMNode> *outLastNode)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
1999-11-17 14:03:25 +03:00
|
|
|
if (!aRoot || !outLastNode) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
1999-09-22 05:23:58 +04:00
|
|
|
*outLastNode = nsnull;
|
1999-11-17 14:03:25 +03:00
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNode> node,next;
|
|
|
|
nsresult rv = GetRightmostChild(aRoot, getter_AddRefs(node));
|
1999-09-22 05:23:58 +04:00
|
|
|
if (NS_FAILED(rv)) return rv;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-11-17 14:03:25 +03:00
|
|
|
if (node && !IsEditable(node))
|
1999-09-22 05:23:58 +04:00
|
|
|
{
|
1999-11-17 14:03:25 +03:00
|
|
|
rv = GetPriorNode(node, PR_TRUE, getter_AddRefs(next));
|
|
|
|
node = next;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
1999-09-22 05:23:58 +04:00
|
|
|
|
1999-11-25 03:16:56 +03:00
|
|
|
if (node.get() != aRoot) *outLastNode = node;
|
1999-11-17 14:03:25 +03:00
|
|
|
|
|
|
|
return rv;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::NotifyDocumentListeners(TDocumentListenerNotification aNotificationType)
|
|
|
|
{
|
|
|
|
if (!mDocStateListeners)
|
1999-08-25 14:51:55 +04:00
|
|
|
return NS_OK; // maybe there just aren't any.
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
PRUint32 numListeners;
|
|
|
|
nsresult rv = mDocStateListeners->Count(&numListeners);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
PRUint32 i;
|
|
|
|
switch (aNotificationType)
|
|
|
|
{
|
|
|
|
case eDocumentCreated:
|
|
|
|
for (i = 0; i < numListeners;i++)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsISupports> iSupports = getter_AddRefs(mDocStateListeners->ElementAt(i));
|
|
|
|
nsCOMPtr<nsIDocumentStateListener> thisListener = do_QueryInterface(iSupports);
|
|
|
|
if (thisListener)
|
|
|
|
{
|
|
|
|
rv = thisListener->NotifyDocumentCreated();
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case eDocumentToBeDestroyed:
|
|
|
|
for (i = 0; i < numListeners;i++)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsISupports> iSupports = getter_AddRefs(mDocStateListeners->ElementAt(i));
|
|
|
|
nsCOMPtr<nsIDocumentStateListener> thisListener = do_QueryInterface(iSupports);
|
|
|
|
if (thisListener)
|
|
|
|
{
|
|
|
|
rv = thisListener->NotifyDocumentWillBeDestroyed();
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case eDocumentStateChanged:
|
|
|
|
{
|
|
|
|
PRBool docIsDirty;
|
|
|
|
rv = GetDocumentModified(&docIsDirty);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
if (docIsDirty == mDocDirtyState)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
mDocDirtyState = (PRInt8)docIsDirty;
|
|
|
|
|
|
|
|
for (i = 0; i < numListeners;i++)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsISupports> iSupports = getter_AddRefs(mDocStateListeners->ElementAt(i));
|
|
|
|
nsCOMPtr<nsIDocumentStateListener> thisListener = do_QueryInterface(iSupports);
|
|
|
|
if (thisListener)
|
|
|
|
{
|
|
|
|
rv = thisListener->NotifyDocumentStateChanged(mDocDirtyState);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
NS_NOTREACHED("Unknown notification");
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsEditor::CreateTxnForInsertText(const nsString & aStringToInsert,
|
|
|
|
nsIDOMCharacterData *aTextNode,
|
1999-10-26 22:54:47 +04:00
|
|
|
PRInt32 aOffset,
|
1999-08-09 05:37:50 +04:00
|
|
|
InsertTextTxn ** aTxn)
|
|
|
|
{
|
1999-10-26 22:54:47 +04:00
|
|
|
if (!aTextNode || !aTxn) return NS_ERROR_NULL_POINTER;
|
1999-08-09 05:37:50 +04:00
|
|
|
nsresult result;
|
|
|
|
|
1999-10-26 22:54:47 +04:00
|
|
|
result = TransactionFactory::GetNewTransaction(InsertTextTxn::GetCID(), (EditTxn **)aTxn);
|
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
if (!*aTxn) return NS_ERROR_OUT_OF_MEMORY;
|
2000-01-04 06:09:41 +03:00
|
|
|
result = (*aTxn)->Init(aTextNode, aOffset, aStringToInsert, this);
|
1999-08-09 05:37:50 +04:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2000-01-04 06:09:41 +03:00
|
|
|
|
1999-03-02 08:30:53 +03:00
|
|
|
NS_IMETHODIMP nsEditor::DeleteText(nsIDOMCharacterData *aElement,
|
1999-02-12 20:18:58 +03:00
|
|
|
PRUint32 aOffset,
|
|
|
|
PRUint32 aLength)
|
|
|
|
{
|
1999-10-08 00:46:50 +04:00
|
|
|
DeleteTextTxn *txn;
|
|
|
|
nsresult result = CreateTxnForDeleteText(aElement, aOffset, aLength, &txn);
|
1999-12-07 11:30:19 +03:00
|
|
|
nsAutoRules beginRulesSniffing(this, kOpDeleteText, nsIEditor::ePrevious);
|
1999-11-25 03:16:56 +03:00
|
|
|
if (NS_SUCCEEDED(result))
|
|
|
|
{
|
2000-01-04 06:09:41 +03:00
|
|
|
// let listeners know whats up
|
1999-11-25 03:16:56 +03:00
|
|
|
PRInt32 i;
|
|
|
|
nsIEditActionListener *listener;
|
|
|
|
if (mActionListeners)
|
|
|
|
{
|
|
|
|
for (i = 0; i < mActionListeners->Count(); i++)
|
|
|
|
{
|
|
|
|
listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
|
|
|
|
if (listener)
|
|
|
|
listener->WillDeleteText(aElement, aOffset, aLength);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
result = Do(txn);
|
|
|
|
|
2000-01-04 06:09:41 +03:00
|
|
|
// let listeners know what happened
|
1999-11-25 03:16:56 +03:00
|
|
|
if (mActionListeners)
|
|
|
|
{
|
|
|
|
for (i = 0; i < mActionListeners->Count(); i++)
|
|
|
|
{
|
|
|
|
listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
|
|
|
|
if (listener)
|
|
|
|
listener->DidDeleteText(aElement, aOffset, aLength, result);
|
|
|
|
}
|
|
|
|
}
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
1999-11-01 18:15:35 +03:00
|
|
|
// The transaction system (if any) has taken ownwership of txn
|
|
|
|
NS_IF_RELEASE(txn);
|
1999-02-12 20:18:58 +03:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-03-02 08:30:53 +03:00
|
|
|
NS_IMETHODIMP nsEditor::CreateTxnForDeleteText(nsIDOMCharacterData *aElement,
|
1999-03-10 22:48:13 +03:00
|
|
|
PRUint32 aOffset,
|
|
|
|
PRUint32 aLength,
|
|
|
|
DeleteTextTxn **aTxn)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
|
|
|
nsresult result=NS_ERROR_NULL_POINTER;
|
|
|
|
if (nsnull != aElement)
|
|
|
|
{
|
1999-07-15 23:13:46 +04:00
|
|
|
result = TransactionFactory::GetNewTransaction(DeleteTextTxn::GetCID(), (EditTxn **)aTxn);
|
1999-02-15 21:25:30 +03:00
|
|
|
if (NS_SUCCEEDED(result)) {
|
1999-02-22 18:53:31 +03:00
|
|
|
result = (*aTxn)->Init(this, aElement, aOffset, aLength);
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-04-08 04:46:10 +04:00
|
|
|
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP nsEditor::CreateTxnForSplitNode(nsIDOMNode *aNode,
|
|
|
|
PRUint32 aOffset,
|
|
|
|
SplitElementTxn **aTxn)
|
|
|
|
{
|
|
|
|
nsresult result=NS_ERROR_NULL_POINTER;
|
|
|
|
if (nsnull != aNode)
|
|
|
|
{
|
|
|
|
result = TransactionFactory::GetNewTransaction(SplitElementTxn::GetCID(), (EditTxn **)aTxn);
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
result = (*aTxn)->Init(this, aNode, aOffset);
|
|
|
|
}
|
|
|
|
}
|
1999-04-08 04:46:10 +04:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP nsEditor::CreateTxnForJoinNode(nsIDOMNode *aLeftNode,
|
|
|
|
nsIDOMNode *aRightNode,
|
|
|
|
JoinElementTxn **aTxn)
|
1999-03-02 08:30:53 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsresult result=NS_ERROR_NULL_POINTER;
|
|
|
|
if ((nsnull != aLeftNode) && (nsnull != aRightNode))
|
1999-03-02 08:30:53 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
result = TransactionFactory::GetNewTransaction(JoinElementTxn::GetCID(), (EditTxn **)aTxn);
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
result = (*aTxn)->Init(this, aLeftNode, aRightNode);
|
1999-03-02 08:30:53 +03:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// END nsEditor core implementation
|
|
|
|
|
|
|
|
#ifdef XP_MAC
|
|
|
|
#pragma mark -
|
2000-03-24 03:26:47 +03:00
|
|
|
#pragma mark nsEditor public static helper methods
|
1999-08-09 05:37:50 +04:00
|
|
|
#pragma mark -
|
|
|
|
#endif
|
|
|
|
|
2000-01-04 06:09:41 +03:00
|
|
|
// BEGIN nsEditor public helper methods
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsEditor::SplitNodeImpl(nsIDOMNode * aExistingRightNode,
|
|
|
|
PRInt32 aOffset,
|
|
|
|
nsIDOMNode* aNewLeftNode,
|
|
|
|
nsIDOMNode* aParent)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (gNoisy) { printf("SplitNodeImpl: left=%p, right=%p, offset=%d\n", aNewLeftNode, aExistingRightNode, aOffset); }
|
|
|
|
|
|
|
|
nsresult result;
|
|
|
|
NS_ASSERTION(((nsnull!=aExistingRightNode) &&
|
|
|
|
(nsnull!=aNewLeftNode) &&
|
|
|
|
(nsnull!=aParent)),
|
|
|
|
"null arg");
|
|
|
|
if ((nsnull!=aExistingRightNode) &&
|
|
|
|
(nsnull!=aNewLeftNode) &&
|
|
|
|
(nsnull!=aParent))
|
|
|
|
{
|
2000-01-04 06:09:41 +03:00
|
|
|
// get selection
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
2000-01-07 01:35:04 +03:00
|
|
|
result = GetSelection(getter_AddRefs(selection));
|
2000-01-04 06:09:41 +03:00
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
if (!selection) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
// remember some selection points
|
|
|
|
nsCOMPtr<nsIDOMNode> selStartNode, selEndNode;
|
|
|
|
PRInt32 selStartOffset, selEndOffset;
|
|
|
|
result = GetStartNodeAndOffset(selection, &selStartNode, &selStartOffset);
|
2000-03-24 03:26:47 +03:00
|
|
|
if (NS_FAILED(result)) selStartNode = nsnull; // if selection is cleared, remember that
|
2000-01-04 06:09:41 +03:00
|
|
|
result = GetEndNodeAndOffset(selection, &selEndNode, &selEndOffset);
|
2000-03-24 03:26:47 +03:00
|
|
|
if (NS_FAILED(result)) selStartNode = nsnull; // if selection is cleared, remember that
|
2000-01-04 06:09:41 +03:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> resultNode;
|
|
|
|
result = aParent->InsertBefore(aNewLeftNode, aExistingRightNode, getter_AddRefs(resultNode));
|
|
|
|
//printf(" after insert\n"); content->List(); // DEBUG
|
|
|
|
if (NS_SUCCEEDED(result))
|
1999-03-02 08:30:53 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
// split the children between the 2 nodes
|
|
|
|
// at this point, aExistingRightNode has all the children
|
|
|
|
// move all the children whose index is < aOffset to aNewLeftNode
|
|
|
|
if (0<=aOffset) // don't bother unless we're going to move at least one child
|
|
|
|
{
|
|
|
|
// if it's a text node, just shuffle around some text
|
|
|
|
nsCOMPtr<nsIDOMCharacterData> rightNodeAsText( do_QueryInterface(aExistingRightNode) );
|
|
|
|
nsCOMPtr<nsIDOMCharacterData> leftNodeAsText( do_QueryInterface(aNewLeftNode) );
|
|
|
|
if (leftNodeAsText && rightNodeAsText)
|
1999-03-17 09:13:46 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
// fix right node
|
1999-09-15 03:44:05 +04:00
|
|
|
nsAutoString leftText;
|
1999-08-09 05:37:50 +04:00
|
|
|
rightNodeAsText->SubstringData(0, aOffset, leftText);
|
|
|
|
rightNodeAsText->DeleteData(0, aOffset);
|
|
|
|
// fix left node
|
|
|
|
leftNodeAsText->SetData(leftText);
|
|
|
|
// moose
|
1999-03-17 09:13:46 +03:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
else
|
|
|
|
{ // otherwise it's an interior node, so shuffle around the children
|
|
|
|
// go through list backwards so deletes don't interfere with the iteration
|
|
|
|
nsCOMPtr<nsIDOMNodeList> childNodes;
|
|
|
|
result = aExistingRightNode->GetChildNodes(getter_AddRefs(childNodes));
|
|
|
|
if ((NS_SUCCEEDED(result)) && (childNodes))
|
1999-03-02 08:30:53 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
PRInt32 i=aOffset-1;
|
|
|
|
for ( ; ((NS_SUCCEEDED(result)) && (0<=i)); i--)
|
1999-07-14 19:24:33 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> childNode;
|
|
|
|
result = childNodes->Item(i, getter_AddRefs(childNode));
|
|
|
|
if ((NS_SUCCEEDED(result)) && (childNode))
|
1999-07-14 19:24:33 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
result = aExistingRightNode->RemoveChild(childNode, getter_AddRefs(resultNode));
|
|
|
|
//printf(" after remove\n"); content->List(); // DEBUG
|
|
|
|
if (NS_SUCCEEDED(result))
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> firstChild;
|
|
|
|
aNewLeftNode->GetFirstChild(getter_AddRefs(firstChild));
|
|
|
|
result = aNewLeftNode->InsertBefore(childNode, firstChild, getter_AddRefs(resultNode));
|
|
|
|
//printf(" after append\n"); content->List(); // DEBUG
|
|
|
|
}
|
1999-03-10 22:48:13 +03:00
|
|
|
}
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
1999-03-02 08:30:53 +03:00
|
|
|
}
|
2000-01-04 06:09:41 +03:00
|
|
|
// handle selection
|
|
|
|
if (GetShouldTxnSetSelection())
|
|
|
|
{
|
|
|
|
// editor wants us to set selection at split point
|
|
|
|
selection->Collapse(aNewLeftNode, aOffset);
|
|
|
|
}
|
2000-03-24 03:26:47 +03:00
|
|
|
else if (selStartNode)
|
2000-01-04 06:09:41 +03:00
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
// else adjust the selection if needed. if selStartNode is null, then there was no selection.
|
2000-01-04 06:09:41 +03:00
|
|
|
// HACK: this is overly simplified - multi-range selections need more work than this
|
2000-01-04 07:28:39 +03:00
|
|
|
if (selStartNode.get() == aExistingRightNode)
|
2000-01-04 06:09:41 +03:00
|
|
|
{
|
|
|
|
if (selStartOffset < aOffset)
|
|
|
|
{
|
|
|
|
selStartNode = aNewLeftNode;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
selStartOffset -= aOffset;
|
|
|
|
}
|
|
|
|
}
|
2000-01-04 07:28:39 +03:00
|
|
|
if (selEndNode.get() == aExistingRightNode)
|
2000-01-04 06:09:41 +03:00
|
|
|
{
|
|
|
|
if (selEndOffset < aOffset)
|
|
|
|
{
|
|
|
|
selEndNode = aNewLeftNode;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
selEndOffset -= aOffset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
selection->Collapse(selStartNode,selStartOffset);
|
|
|
|
selection->Extend(selEndNode,selEndOffset);
|
|
|
|
}
|
1999-03-02 08:30:53 +03:00
|
|
|
}
|
1999-03-17 09:13:46 +03:00
|
|
|
}
|
1999-03-02 08:30:53 +03:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
else
|
|
|
|
result = NS_ERROR_INVALID_ARG;
|
1999-02-12 20:18:58 +03:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsresult
|
|
|
|
nsEditor::JoinNodesImpl(nsIDOMNode * aNodeToKeep,
|
|
|
|
nsIDOMNode * aNodeToJoin,
|
|
|
|
nsIDOMNode * aParent,
|
|
|
|
PRBool aNodeToKeepIsFirst)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsresult result = NS_OK;
|
2000-01-04 06:09:41 +03:00
|
|
|
NS_ASSERTION(aNodeToKeep && aNodeToJoin && aParent, "null arg");
|
|
|
|
if (aNodeToKeep && aNodeToJoin && aParent)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
2000-01-04 06:09:41 +03:00
|
|
|
// get selection
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
|
|
|
GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
if (!selection) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
// remember some selection points
|
2000-01-05 15:24:10 +03:00
|
|
|
nsCOMPtr<nsIDOMNode> selStartNode, selEndNode, parent;
|
|
|
|
PRInt32 selStartOffset, selEndOffset, joinOffset, keepOffset;
|
2000-01-04 06:09:41 +03:00
|
|
|
result = GetStartNodeAndOffset(selection, &selStartNode, &selStartOffset);
|
2000-03-24 03:26:47 +03:00
|
|
|
if (NS_FAILED(result)) selStartNode = nsnull;
|
2000-01-04 06:09:41 +03:00
|
|
|
result = GetEndNodeAndOffset(selection, &selEndNode, &selEndOffset);
|
2000-03-24 03:26:47 +03:00
|
|
|
if (NS_FAILED(result)) selStartNode = nsnull;
|
|
|
|
|
|
|
|
|
2000-01-04 06:09:41 +03:00
|
|
|
PRUint32 firstNodeLength;
|
|
|
|
nsCOMPtr<nsIDOMNode> leftNode = aNodeToJoin;
|
|
|
|
if (aNodeToKeepIsFirst) leftNode = aNodeToKeep;
|
|
|
|
result = GetLengthOfDOMNode(leftNode, firstNodeLength);
|
|
|
|
if (NS_FAILED(result)) return result;
|
2000-01-05 15:24:10 +03:00
|
|
|
result = GetNodeLocation(aNodeToJoin, &parent, &joinOffset);
|
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
result = GetNodeLocation(aNodeToKeep, &parent, &keepOffset);
|
|
|
|
if (NS_FAILED(result)) return result;
|
2000-01-04 06:09:41 +03:00
|
|
|
|
2000-01-05 15:24:10 +03:00
|
|
|
// if selection endpoint is between the nodes, remember it as being
|
|
|
|
// in the one that is going away instead. This simplifies later selection
|
|
|
|
// adjustment logic at end of this method.
|
2000-03-24 03:26:47 +03:00
|
|
|
if (selStartNode)
|
2000-01-05 15:24:10 +03:00
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
if (selStartNode == parent)
|
2000-01-05 15:24:10 +03:00
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
if (aNodeToKeepIsFirst)
|
2000-01-05 15:24:10 +03:00
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
if ((selStartOffset > keepOffset) && (selStartOffset <= joinOffset))
|
|
|
|
{
|
|
|
|
selStartNode = aNodeToJoin;
|
|
|
|
selStartOffset = 0;
|
|
|
|
}
|
2000-01-05 15:24:10 +03:00
|
|
|
}
|
2000-03-24 03:26:47 +03:00
|
|
|
else
|
2000-01-05 15:24:10 +03:00
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
if ((selStartOffset > joinOffset) && (selStartOffset <= keepOffset))
|
|
|
|
{
|
|
|
|
selStartNode = aNodeToJoin;
|
|
|
|
selStartOffset = firstNodeLength;
|
|
|
|
}
|
2000-01-05 15:24:10 +03:00
|
|
|
}
|
|
|
|
}
|
2000-03-24 03:26:47 +03:00
|
|
|
if (selEndNode == parent)
|
2000-01-05 15:24:10 +03:00
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
if (aNodeToKeepIsFirst)
|
2000-01-05 15:24:10 +03:00
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
if ((selEndOffset > keepOffset) && (selEndOffset <= joinOffset))
|
|
|
|
{
|
|
|
|
selEndNode = aNodeToJoin;
|
|
|
|
selEndOffset = 0;
|
|
|
|
}
|
2000-01-05 15:24:10 +03:00
|
|
|
}
|
2000-03-24 03:26:47 +03:00
|
|
|
else
|
2000-01-05 15:24:10 +03:00
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
if ((selEndOffset > joinOffset) && (selEndOffset <= keepOffset))
|
|
|
|
{
|
|
|
|
selEndNode = aNodeToJoin;
|
|
|
|
selEndOffset = firstNodeLength;
|
|
|
|
}
|
2000-01-05 15:24:10 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// ok, ready to do join now.
|
1999-08-09 05:37:50 +04:00
|
|
|
// if it's a text node, just shuffle around some text
|
|
|
|
nsCOMPtr<nsIDOMCharacterData> keepNodeAsText( do_QueryInterface(aNodeToKeep) );
|
|
|
|
nsCOMPtr<nsIDOMCharacterData> joinNodeAsText( do_QueryInterface(aNodeToJoin) );
|
|
|
|
if (keepNodeAsText && joinNodeAsText)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-09-15 03:44:05 +04:00
|
|
|
nsAutoString rightText;
|
|
|
|
nsAutoString leftText;
|
1999-08-09 05:37:50 +04:00
|
|
|
if (aNodeToKeepIsFirst)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
keepNodeAsText->GetData(leftText);
|
|
|
|
joinNodeAsText->GetData(rightText);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
keepNodeAsText->GetData(rightText);
|
|
|
|
joinNodeAsText->GetData(leftText);
|
|
|
|
}
|
|
|
|
leftText += rightText;
|
|
|
|
keepNodeAsText->SetData(leftText);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // otherwise it's an interior node, so shuffle around the children
|
|
|
|
nsCOMPtr<nsIDOMNodeList> childNodes;
|
|
|
|
result = aNodeToJoin->GetChildNodes(getter_AddRefs(childNodes));
|
|
|
|
if ((NS_SUCCEEDED(result)) && (childNodes))
|
|
|
|
{
|
|
|
|
PRInt32 i; // must be signed int!
|
|
|
|
PRUint32 childCount=0;
|
|
|
|
nsCOMPtr<nsIDOMNode> firstNode; //only used if aNodeToKeepIsFirst is false
|
2000-01-04 06:09:41 +03:00
|
|
|
childNodes->GetLength(&childCount);
|
|
|
|
if (!aNodeToKeepIsFirst)
|
1999-08-09 05:37:50 +04:00
|
|
|
{ // remember the first child in aNodeToKeep, we'll insert all the children of aNodeToJoin in front of it
|
|
|
|
result = aNodeToKeep->GetFirstChild(getter_AddRefs(firstNode));
|
|
|
|
// GetFirstChild returns nsnull firstNode if aNodeToKeep has no children, that's ok.
|
|
|
|
}
|
|
|
|
nsCOMPtr<nsIDOMNode> resultNode;
|
|
|
|
// have to go through the list backwards to keep deletes from interfering with iteration
|
|
|
|
nsCOMPtr<nsIDOMNode> previousChild;
|
|
|
|
for (i=childCount-1; ((NS_SUCCEEDED(result)) && (0<=i)); i--)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> childNode;
|
|
|
|
result = childNodes->Item(i, getter_AddRefs(childNode));
|
|
|
|
if ((NS_SUCCEEDED(result)) && (childNode))
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
2000-01-04 06:09:41 +03:00
|
|
|
if (aNodeToKeepIsFirst)
|
1999-08-09 05:37:50 +04:00
|
|
|
{ // append children of aNodeToJoin
|
|
|
|
//was result = aNodeToKeep->AppendChild(childNode, getter_AddRefs(resultNode));
|
|
|
|
result = aNodeToKeep->InsertBefore(childNode, previousChild, getter_AddRefs(resultNode));
|
|
|
|
previousChild = do_QueryInterface(childNode);
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
|
|
|
else
|
1999-08-09 05:37:50 +04:00
|
|
|
{ // prepend children of aNodeToJoin
|
|
|
|
//was result = aNodeToKeep->InsertBefore(childNode, firstNode, getter_AddRefs(resultNode));
|
|
|
|
result = aNodeToKeep->InsertBefore(childNode, firstNode, getter_AddRefs(resultNode));
|
|
|
|
firstNode = do_QueryInterface(childNode);
|
|
|
|
}
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
else if (!childNodes) {
|
|
|
|
result = NS_ERROR_NULL_POINTER;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (NS_SUCCEEDED(result))
|
|
|
|
{ // delete the extra node
|
|
|
|
nsCOMPtr<nsIDOMNode> resultNode;
|
|
|
|
result = aParent->RemoveChild(aNodeToJoin, getter_AddRefs(resultNode));
|
2000-01-04 06:09:41 +03:00
|
|
|
|
|
|
|
if (GetShouldTxnSetSelection())
|
|
|
|
{
|
|
|
|
// editor wants us to set selection at join point
|
|
|
|
selection->Collapse(aNodeToKeep, firstNodeLength);
|
|
|
|
}
|
2000-03-24 03:26:47 +03:00
|
|
|
else if (selStartNode)
|
2000-01-04 06:09:41 +03:00
|
|
|
{
|
|
|
|
// and adjust the selection if needed
|
|
|
|
// HACK: this is overly simplified - multi-range selections need more work than this
|
2000-05-06 00:42:36 +04:00
|
|
|
PRBool bNeedToAdjust = PR_FALSE;
|
2000-01-04 07:28:39 +03:00
|
|
|
if (selStartNode.get() == aNodeToJoin)
|
2000-01-04 06:09:41 +03:00
|
|
|
{
|
2000-05-06 00:42:36 +04:00
|
|
|
bNeedToAdjust = PR_TRUE;
|
2000-01-04 06:09:41 +03:00
|
|
|
selStartNode = aNodeToKeep;
|
|
|
|
if (aNodeToKeepIsFirst)
|
|
|
|
{
|
|
|
|
selStartOffset += firstNodeLength;
|
|
|
|
}
|
|
|
|
}
|
2000-01-05 15:24:10 +03:00
|
|
|
else if ((selStartNode.get() == aNodeToKeep) && !aNodeToKeepIsFirst)
|
|
|
|
{
|
2000-05-06 00:42:36 +04:00
|
|
|
bNeedToAdjust = PR_TRUE;
|
2000-01-05 15:24:10 +03:00
|
|
|
selStartOffset += firstNodeLength;
|
|
|
|
}
|
2000-05-06 00:42:36 +04:00
|
|
|
|
|
|
|
if (bNeedToAdjust)
|
|
|
|
selection->Collapse(selStartNode,selStartOffset);
|
|
|
|
|
|
|
|
bNeedToAdjust = PR_FALSE;
|
|
|
|
|
2000-01-04 07:28:39 +03:00
|
|
|
if (selEndNode.get() == aNodeToJoin)
|
2000-01-04 06:09:41 +03:00
|
|
|
{
|
2000-05-06 00:42:36 +04:00
|
|
|
bNeedToAdjust = PR_TRUE;
|
2000-01-04 06:09:41 +03:00
|
|
|
selEndNode = aNodeToKeep;
|
|
|
|
if (aNodeToKeepIsFirst)
|
|
|
|
{
|
|
|
|
selEndOffset += firstNodeLength;
|
|
|
|
}
|
|
|
|
}
|
2000-01-05 15:24:10 +03:00
|
|
|
else if ((selEndNode.get() == aNodeToKeep) && !aNodeToKeepIsFirst)
|
|
|
|
{
|
2000-05-06 00:42:36 +04:00
|
|
|
bNeedToAdjust = PR_TRUE;
|
2000-01-05 15:24:10 +03:00
|
|
|
selEndOffset += firstNodeLength;
|
|
|
|
}
|
2000-05-06 00:42:36 +04:00
|
|
|
|
|
|
|
if (bNeedToAdjust)
|
|
|
|
selection->Extend(selEndNode,selEndOffset);
|
2000-01-04 06:09:41 +03:00
|
|
|
}
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
else
|
|
|
|
result = NS_ERROR_INVALID_ARG;
|
1999-02-12 20:18:58 +03:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsresult
|
|
|
|
nsEditor::GetChildOffset(nsIDOMNode *aChild, nsIDOMNode *aParent, PRInt32 &aOffset)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_ASSERTION((aChild && aParent), "bad args");
|
2000-03-24 03:26:47 +03:00
|
|
|
if (!aChild || !aParent) return NS_ERROR_NULL_POINTER;
|
|
|
|
nsCOMPtr<nsIContent> content = do_QueryInterface(aParent);
|
|
|
|
nsCOMPtr<nsIContent> cChild = do_QueryInterface(aChild);
|
|
|
|
if (!cChild || !content) return NS_ERROR_NULL_POINTER;
|
|
|
|
nsresult res = content->IndexOf(cChild, aOffset);
|
|
|
|
return res;
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsresult
|
|
|
|
nsEditor::GetNodeLocation(nsIDOMNode *inChild, nsCOMPtr<nsIDOMNode> *outParent, PRInt32 *outOffset)
|
1999-02-17 22:42:29 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_ASSERTION((inChild && outParent && outOffset), "bad args");
|
|
|
|
nsresult result = NS_ERROR_NULL_POINTER;
|
|
|
|
if (inChild && outParent && outOffset)
|
1999-04-27 21:14:28 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
result = inChild->GetParentNode(getter_AddRefs(*outParent));
|
|
|
|
if ((NS_SUCCEEDED(result)) && (*outParent))
|
1999-04-27 21:14:28 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
result = GetChildOffset(inChild, *outParent, *outOffset);
|
1999-04-27 21:14:28 +04:00
|
|
|
}
|
|
|
|
}
|
1999-02-17 22:42:29 +03:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
// returns the number of things inside aNode.
|
|
|
|
// If aNode is text, returns number of characters. If not, returns number of children nodes.
|
|
|
|
nsresult
|
|
|
|
nsEditor::GetLengthOfDOMNode(nsIDOMNode *aNode, PRUint32 &aCount)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
aCount = 0;
|
|
|
|
if (!aNode) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
nsresult result=NS_OK;
|
|
|
|
nsCOMPtr<nsIDOMCharacterData>nodeAsChar;
|
|
|
|
nodeAsChar = do_QueryInterface(aNode);
|
|
|
|
if (nodeAsChar) {
|
|
|
|
nodeAsChar->GetLength(&aCount);
|
|
|
|
}
|
|
|
|
else
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
PRBool hasChildNodes;
|
|
|
|
aNode->HasChildNodes(&hasChildNodes);
|
|
|
|
if (PR_TRUE==hasChildNodes)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNodeList>nodeList;
|
|
|
|
result = aNode->GetChildNodes(getter_AddRefs(nodeList));
|
|
|
|
if (NS_SUCCEEDED(result) && nodeList) {
|
|
|
|
nodeList->GetLength(&aCount);
|
|
|
|
}
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-09-01 05:22:37 +04:00
|
|
|
// Non-static version for the nsIEditor interface and JavaScript
|
|
|
|
NS_IMETHODIMP
|
1999-09-01 23:58:25 +04:00
|
|
|
nsEditor::NodeIsBlock(nsIDOMNode *aNode, PRBool &aIsBlock)
|
1999-09-01 05:22:37 +04:00
|
|
|
{
|
1999-09-01 23:58:25 +04:00
|
|
|
if (!aNode) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
return IsNodeBlock(aNode, aIsBlock);
|
1999-09-01 05:22:37 +04:00
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
// The list of block nodes is shorter, so do the real work here...
|
1999-09-01 05:22:37 +04:00
|
|
|
nsresult
|
1999-08-09 05:37:50 +04:00
|
|
|
nsEditor::IsNodeBlock(nsIDOMNode *aNode, PRBool &aIsBlock)
|
1999-02-17 22:42:29 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
// this is a content-based implementation
|
|
|
|
if (!aNode) { return NS_ERROR_NULL_POINTER; }
|
1999-04-27 21:14:28 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsresult result = NS_ERROR_FAILURE;
|
|
|
|
aIsBlock = PR_FALSE;
|
|
|
|
nsCOMPtr<nsIDOMElement>element;
|
|
|
|
element = do_QueryInterface(aNode);
|
|
|
|
if (element)
|
1999-04-27 21:14:28 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsAutoString tagName;
|
|
|
|
result = element->GetTagName(tagName);
|
|
|
|
if (NS_SUCCEEDED(result))
|
1999-04-27 21:14:28 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
tagName.ToLowerCase();
|
|
|
|
nsIAtom *tagAtom = NS_NewAtom(tagName);
|
|
|
|
if (!tagAtom) { return NS_ERROR_NULL_POINTER; }
|
1999-04-27 21:14:28 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
if (tagAtom==nsIEditProperty::p ||
|
|
|
|
tagAtom==nsIEditProperty::div ||
|
|
|
|
tagAtom==nsIEditProperty::blockquote ||
|
|
|
|
tagAtom==nsIEditProperty::h1 ||
|
|
|
|
tagAtom==nsIEditProperty::h2 ||
|
|
|
|
tagAtom==nsIEditProperty::h3 ||
|
|
|
|
tagAtom==nsIEditProperty::h4 ||
|
|
|
|
tagAtom==nsIEditProperty::h5 ||
|
|
|
|
tagAtom==nsIEditProperty::h6 ||
|
|
|
|
tagAtom==nsIEditProperty::ul ||
|
|
|
|
tagAtom==nsIEditProperty::ol ||
|
|
|
|
tagAtom==nsIEditProperty::dl ||
|
|
|
|
tagAtom==nsIEditProperty::pre ||
|
|
|
|
tagAtom==nsIEditProperty::noscript ||
|
|
|
|
tagAtom==nsIEditProperty::form ||
|
|
|
|
tagAtom==nsIEditProperty::hr ||
|
|
|
|
tagAtom==nsIEditProperty::table ||
|
|
|
|
tagAtom==nsIEditProperty::fieldset ||
|
|
|
|
tagAtom==nsIEditProperty::address ||
|
|
|
|
tagAtom==nsIEditProperty::body ||
|
|
|
|
tagAtom==nsIEditProperty::tr ||
|
|
|
|
tagAtom==nsIEditProperty::td ||
|
|
|
|
tagAtom==nsIEditProperty::th ||
|
|
|
|
tagAtom==nsIEditProperty::caption ||
|
|
|
|
tagAtom==nsIEditProperty::col ||
|
|
|
|
tagAtom==nsIEditProperty::colgroup ||
|
2000-02-03 05:06:10 +03:00
|
|
|
tagAtom==nsIEditProperty::tbody ||
|
1999-08-09 05:37:50 +04:00
|
|
|
tagAtom==nsIEditProperty::thead ||
|
|
|
|
tagAtom==nsIEditProperty::tfoot ||
|
|
|
|
tagAtom==nsIEditProperty::li ||
|
|
|
|
tagAtom==nsIEditProperty::dt ||
|
|
|
|
tagAtom==nsIEditProperty::dd ||
|
|
|
|
tagAtom==nsIEditProperty::legend )
|
|
|
|
{
|
|
|
|
aIsBlock = PR_TRUE;
|
|
|
|
}
|
1999-10-07 00:27:41 +04:00
|
|
|
NS_RELEASE(tagAtom);
|
1999-08-09 05:37:50 +04:00
|
|
|
result = NS_OK;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// We don't have an element -- probably a text node
|
|
|
|
nsCOMPtr<nsIDOMCharacterData>nodeAsText = do_QueryInterface(aNode);
|
|
|
|
if (nodeAsText)
|
1999-04-27 21:14:28 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
aIsBlock = PR_FALSE;
|
|
|
|
result = NS_OK;
|
1999-04-27 21:14:28 +04:00
|
|
|
}
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ...and simply assume non-block element is inline
|
|
|
|
nsresult
|
|
|
|
nsEditor::IsNodeInline(nsIDOMNode *aNode, PRBool &aIsInline)
|
|
|
|
{
|
|
|
|
// this is a content-based implementation
|
|
|
|
if (!aNode) { return NS_ERROR_NULL_POINTER; }
|
1999-04-27 21:14:28 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsresult result;
|
|
|
|
aIsInline = PR_FALSE;
|
|
|
|
PRBool IsBlock = PR_FALSE;
|
|
|
|
result = IsNodeBlock(aNode, IsBlock);
|
|
|
|
aIsInline = !IsBlock;
|
1999-02-17 22:42:29 +03:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-08-31 17:55:18 +04:00
|
|
|
nsresult
|
|
|
|
nsEditor::GetPriorNode(nsIDOMNode *aParentNode,
|
|
|
|
PRInt32 aOffset,
|
|
|
|
PRBool aEditableNode,
|
|
|
|
nsIDOMNode **aResultNode)
|
|
|
|
{
|
|
|
|
// just another version of GetPriorNode that takes a {parent, offset}
|
|
|
|
// instead of a node
|
|
|
|
nsresult result = NS_OK;
|
|
|
|
if (!aParentNode || !aResultNode) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
|
1999-09-30 00:08:15 +04:00
|
|
|
// if we are at beginning of node, or it is a textnode, then just look before it
|
|
|
|
if (!aOffset || IsTextNode(aParentNode))
|
1999-08-31 17:55:18 +04:00
|
|
|
{
|
|
|
|
return GetPriorNode(aParentNode, aEditableNode, aResultNode);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// else look before the child at 'aOffset'
|
|
|
|
nsCOMPtr<nsIDOMNode> child = GetChildAt(aParentNode, aOffset);
|
|
|
|
if (child)
|
|
|
|
return GetPriorNode(child, aEditableNode, aResultNode);
|
|
|
|
// unless there isn't one, in which case we are at the end of the node
|
|
|
|
// and want the deep-right child.
|
|
|
|
else
|
|
|
|
{
|
1999-09-11 03:31:43 +04:00
|
|
|
result = GetRightmostChild(aParentNode, aResultNode);
|
1999-08-31 17:55:18 +04:00
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
if (!aEditableNode) return result;
|
|
|
|
if (IsEditable(*aResultNode)) return result;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// restart the search from the non-editable node we just found
|
|
|
|
nsCOMPtr<nsIDOMNode> notEditableNode = do_QueryInterface(*aResultNode);
|
2000-03-23 04:14:49 +03:00
|
|
|
NS_IF_RELEASE(*aResultNode);
|
1999-08-31 17:55:18 +04:00
|
|
|
return GetPriorNode(notEditableNode, aEditableNode, aResultNode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsEditor::GetNextNode(nsIDOMNode *aParentNode,
|
|
|
|
PRInt32 aOffset,
|
|
|
|
PRBool aEditableNode,
|
|
|
|
nsIDOMNode **aResultNode)
|
|
|
|
{
|
|
|
|
// just another version of GetNextNode that takes a {parent, offset}
|
|
|
|
// instead of a node
|
|
|
|
nsresult result = NS_OK;
|
|
|
|
if (!aParentNode || !aResultNode) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
|
2000-05-10 01:06:49 +04:00
|
|
|
*aResultNode = nsnull;
|
|
|
|
|
1999-09-30 00:08:15 +04:00
|
|
|
// if aParentNode is a text node, use it's location instead
|
|
|
|
if (IsTextNode(aParentNode))
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> parent;
|
|
|
|
nsEditor::GetNodeLocation(aParentNode, &parent, &aOffset);
|
|
|
|
aParentNode = parent;
|
|
|
|
aOffset++; // _after_ the text node
|
|
|
|
}
|
1999-08-31 17:55:18 +04:00
|
|
|
// look at the child at 'aOffset'
|
|
|
|
nsCOMPtr<nsIDOMNode> child = GetChildAt(aParentNode, aOffset);
|
|
|
|
if (child)
|
|
|
|
{
|
1999-11-17 14:30:39 +03:00
|
|
|
result = GetLeftmostChild(child, aResultNode);
|
1999-08-31 17:55:18 +04:00
|
|
|
if (NS_FAILED(result)) return result;
|
2000-05-10 01:06:49 +04:00
|
|
|
if (!IsDescendantOfBody(*aResultNode))
|
|
|
|
{
|
2000-06-15 06:14:16 +04:00
|
|
|
NS_RELEASE(*aResultNode); // assigns nsnull
|
2000-05-10 01:06:49 +04:00
|
|
|
return result;
|
|
|
|
}
|
1999-08-31 17:55:18 +04:00
|
|
|
if (!aEditableNode) return result;
|
2000-05-10 01:06:49 +04:00
|
|
|
if (IsEditable(*aResultNode)) return result;
|
1999-08-31 17:55:18 +04:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// restart the search from the non-editable node we just found
|
|
|
|
nsCOMPtr<nsIDOMNode> notEditableNode = do_QueryInterface(*aResultNode);
|
2000-03-23 04:14:49 +03:00
|
|
|
NS_IF_RELEASE(*aResultNode);
|
1999-08-31 17:55:18 +04:00
|
|
|
return GetNextNode(notEditableNode, aEditableNode, aResultNode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// unless there isn't one, in which case we are at the end of the node
|
|
|
|
// and want the next one.
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return GetNextNode(aParentNode, aEditableNode, aResultNode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
1999-06-16 09:02:43 +04:00
|
|
|
nsresult
|
1999-08-09 05:37:50 +04:00
|
|
|
nsEditor::GetPriorNode(nsIDOMNode *aCurrentNode,
|
|
|
|
PRBool aEditableNode,
|
|
|
|
nsIDOMNode **aResultNode)
|
1999-06-16 09:02:43 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsresult result;
|
|
|
|
if (!aCurrentNode || !aResultNode) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
|
|
|
|
*aResultNode = nsnull; // init out-param
|
|
|
|
|
|
|
|
// if aCurrentNode has a left sibling, return that sibling's rightmost child (or itself if it has no children)
|
2000-03-23 04:14:49 +03:00
|
|
|
nsCOMPtr<nsIDOMNode> prevSibling;
|
|
|
|
result = aCurrentNode->GetPreviousSibling(getter_AddRefs(prevSibling));
|
|
|
|
if ((NS_SUCCEEDED(result)) && prevSibling)
|
1999-06-16 09:02:43 +04:00
|
|
|
{
|
2000-03-23 04:14:49 +03:00
|
|
|
result = GetRightmostChild(prevSibling, aResultNode);
|
1999-08-09 05:37:50 +04:00
|
|
|
if (NS_FAILED(result)) { return result; }
|
2000-05-10 01:06:49 +04:00
|
|
|
if (!IsDescendantOfBody(*aResultNode))
|
|
|
|
{
|
2000-06-15 06:14:16 +04:00
|
|
|
NS_RELEASE(*aResultNode); // assigns nsnull
|
2000-05-10 01:06:49 +04:00
|
|
|
return result;
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
if (PR_FALSE==aEditableNode) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
if (PR_TRUE==IsEditable(*aResultNode)) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // restart the search from the non-editable node we just found
|
|
|
|
nsCOMPtr<nsIDOMNode> notEditableNode = do_QueryInterface(*aResultNode);
|
2000-03-23 04:14:49 +03:00
|
|
|
NS_IF_RELEASE(*aResultNode);
|
1999-08-09 05:37:50 +04:00
|
|
|
return GetPriorNode(notEditableNode, aEditableNode, aResultNode);
|
1999-06-16 09:02:43 +04:00
|
|
|
}
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
// otherwise, walk up the parent change until there is a child that comes before
|
|
|
|
// the ancestor of aCurrentNode. Then return that node's rightmost child
|
|
|
|
nsCOMPtr<nsIDOMNode> parent = do_QueryInterface(aCurrentNode);
|
|
|
|
do {
|
|
|
|
nsCOMPtr<nsIDOMNode> node(parent);
|
|
|
|
result = node->GetParentNode(getter_AddRefs(parent));
|
|
|
|
if ((NS_SUCCEEDED(result)) && parent)
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
result = parent->GetPreviousSibling(getter_AddRefs(node));
|
|
|
|
if ((NS_SUCCEEDED(result)) && node)
|
|
|
|
{
|
|
|
|
result = GetRightmostChild(node, aResultNode);
|
|
|
|
if (NS_FAILED(result)) { return result; }
|
2000-05-10 01:06:49 +04:00
|
|
|
if (!IsDescendantOfBody(*aResultNode))
|
|
|
|
{
|
2000-06-15 06:14:16 +04:00
|
|
|
NS_RELEASE(*aResultNode); // assigns nsnull
|
2000-05-10 01:06:49 +04:00
|
|
|
return result;
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
if (PR_FALSE==aEditableNode) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
if (PR_TRUE==IsEditable(*aResultNode)) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // restart the search from the non-editable node we just found
|
|
|
|
nsCOMPtr<nsIDOMNode> notEditableNode = do_QueryInterface(*aResultNode);
|
2000-03-23 04:14:49 +03:00
|
|
|
NS_IF_RELEASE(*aResultNode);
|
1999-08-09 05:37:50 +04:00
|
|
|
return GetPriorNode(notEditableNode, aEditableNode, aResultNode);
|
|
|
|
}
|
1999-03-14 03:31:35 +03:00
|
|
|
}
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
} while ((NS_SUCCEEDED(result)) && parent);
|
|
|
|
|
1999-05-05 08:05:19 +04:00
|
|
|
return result;
|
1999-03-14 03:31:35 +03:00
|
|
|
}
|
|
|
|
|
1999-06-25 07:18:42 +04:00
|
|
|
nsresult
|
1999-08-09 05:37:50 +04:00
|
|
|
nsEditor::GetNextNode(nsIDOMNode *aCurrentNode,
|
|
|
|
PRBool aEditableNode,
|
|
|
|
nsIDOMNode **aResultNode)
|
|
|
|
{
|
|
|
|
nsresult result;
|
|
|
|
*aResultNode = nsnull;
|
|
|
|
// if aCurrentNode has a right sibling, return that sibling's leftmost child (or itself if it has no children)
|
2000-03-24 03:26:47 +03:00
|
|
|
nsCOMPtr<nsIDOMNode> nextSibling;
|
|
|
|
result = aCurrentNode->GetNextSibling(getter_AddRefs(nextSibling));
|
|
|
|
if ((NS_SUCCEEDED(result)) && nextSibling)
|
1999-03-02 08:30:53 +03:00
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
result = GetLeftmostChild(nextSibling, aResultNode);
|
1999-08-09 05:37:50 +04:00
|
|
|
if (NS_FAILED(result)) { return result; }
|
2000-05-10 01:06:49 +04:00
|
|
|
if (!IsDescendantOfBody(*aResultNode))
|
|
|
|
{
|
2000-06-15 06:14:16 +04:00
|
|
|
NS_RELEASE(*aResultNode); // assigns nsnull
|
2000-05-10 01:06:49 +04:00
|
|
|
return result;
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
if (PR_FALSE==aEditableNode) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
if (PR_TRUE==IsEditable(*aResultNode)) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // restart the search from the non-editable node we just found
|
|
|
|
nsCOMPtr<nsIDOMNode> notEditableNode = do_QueryInterface(*aResultNode);
|
2000-03-24 03:26:47 +03:00
|
|
|
NS_IF_RELEASE(*aResultNode);
|
1999-08-09 05:37:50 +04:00
|
|
|
return GetNextNode(notEditableNode, aEditableNode, aResultNode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// otherwise, walk up the parent change until there is a child that comes after
|
|
|
|
// the ancestor of aCurrentNode. Then return that node's leftmost child
|
1999-06-25 07:18:42 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> parent(do_QueryInterface(aCurrentNode));
|
|
|
|
do {
|
|
|
|
nsCOMPtr<nsIDOMNode> node(parent);
|
|
|
|
result = node->GetParentNode(getter_AddRefs(parent));
|
|
|
|
if ((NS_SUCCEEDED(result)) && parent)
|
|
|
|
{
|
|
|
|
result = parent->GetNextSibling(getter_AddRefs(node));
|
|
|
|
if ((NS_SUCCEEDED(result)) && node)
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
result = GetLeftmostChild(node, aResultNode);
|
|
|
|
if (NS_FAILED(result)) { return result; }
|
2000-05-10 01:06:49 +04:00
|
|
|
if (!IsDescendantOfBody(*aResultNode))
|
|
|
|
{
|
2000-06-15 06:14:16 +04:00
|
|
|
NS_RELEASE(*aResultNode); // assigns nsnull
|
2000-05-10 01:06:49 +04:00
|
|
|
return result;
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
if (PR_FALSE==aEditableNode) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
if (PR_TRUE==IsEditable(*aResultNode)) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // restart the search from the non-editable node we just found
|
|
|
|
nsCOMPtr<nsIDOMNode> notEditableNode = do_QueryInterface(*aResultNode);
|
2000-03-23 04:14:49 +03:00
|
|
|
NS_IF_RELEASE(*aResultNode);
|
1999-08-09 05:37:50 +04:00
|
|
|
return GetNextNode(notEditableNode, aEditableNode, aResultNode);
|
|
|
|
}
|
1999-03-02 08:30:53 +03:00
|
|
|
}
|
1999-07-23 04:52:17 +04:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
} while ((NS_SUCCEEDED(result)) && parent);
|
|
|
|
|
1999-06-25 07:18:42 +04:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
1999-08-09 05:37:50 +04:00
|
|
|
nsEditor::GetRightmostChild(nsIDOMNode *aCurrentNode, nsIDOMNode **aResultNode)
|
1999-06-25 07:18:42 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsresult result = NS_OK;
|
|
|
|
nsCOMPtr<nsIDOMNode> resultNode(do_QueryInterface(aCurrentNode));
|
|
|
|
PRBool hasChildren;
|
|
|
|
resultNode->HasChildNodes(&hasChildren);
|
|
|
|
while ((NS_SUCCEEDED(result)) && (PR_TRUE==hasChildren))
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> temp(resultNode);
|
|
|
|
temp->GetLastChild(getter_AddRefs(resultNode));
|
|
|
|
resultNode->HasChildNodes(&hasChildren);
|
|
|
|
}
|
1999-06-25 07:18:42 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
*aResultNode = resultNode;
|
|
|
|
NS_ADDREF(*aResultNode);
|
|
|
|
}
|
1999-06-25 07:18:42 +04:00
|
|
|
return result;
|
1999-05-05 08:05:19 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
1999-08-09 05:37:50 +04:00
|
|
|
nsEditor::GetLeftmostChild(nsIDOMNode *aCurrentNode, nsIDOMNode **aResultNode)
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
|
|
|
nsresult result = NS_OK;
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> resultNode(do_QueryInterface(aCurrentNode));
|
|
|
|
PRBool hasChildren;
|
|
|
|
resultNode->HasChildNodes(&hasChildren);
|
|
|
|
while ((NS_SUCCEEDED(result)) && (PR_TRUE==hasChildren))
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> temp(resultNode);
|
|
|
|
temp->GetFirstChild(getter_AddRefs(resultNode));
|
|
|
|
resultNode->HasChildNodes(&hasChildren);
|
1999-05-05 08:05:19 +04:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
*aResultNode = resultNode;
|
|
|
|
NS_ADDREF(*aResultNode);
|
1999-06-08 10:04:51 +04:00
|
|
|
}
|
1999-05-05 08:05:19 +04:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
PRBool
|
|
|
|
nsEditor::NodeIsType(nsIDOMNode *aNode, nsIAtom *aTag)
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMElement>element;
|
|
|
|
element = do_QueryInterface(aNode);
|
|
|
|
if (element)
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsAutoString tag;
|
|
|
|
element->GetTagName(tag);
|
1999-09-04 02:21:29 +04:00
|
|
|
const PRUnichar *unicodeString;
|
1999-08-21 11:07:00 +04:00
|
|
|
aTag->GetUnicode(&unicodeString);
|
|
|
|
if (tag.EqualsIgnoreCase(unicodeString))
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
return PR_TRUE;
|
1999-05-05 08:05:19 +04:00
|
|
|
}
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
return PR_FALSE;
|
1999-05-05 08:05:19 +04:00
|
|
|
}
|
|
|
|
|
fixes:
14753, 29843, 39864, 40141,
40139, 36679, 39542, 34729,
34855, 37216, 39292, 26447
r=sfraser,cmanske,fm; a=beppe
2000-05-25 03:00:24 +04:00
|
|
|
PRBool
|
|
|
|
nsEditor::NodeIsType(nsIDOMNode *aNode, const nsString &aTagStr)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMElement>element;
|
|
|
|
element = do_QueryInterface(aNode);
|
|
|
|
if (element)
|
|
|
|
{
|
|
|
|
nsAutoString tag;
|
|
|
|
element->GetTagName(tag);
|
|
|
|
if (tag.EqualsIgnoreCase(aTagStr))
|
|
|
|
{
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
PRBool
|
2000-02-08 15:53:34 +03:00
|
|
|
nsEditor::CanContainTag(nsIDOMNode* aParent, const nsString &aChildTag)
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
1999-09-15 03:44:05 +04:00
|
|
|
nsAutoString parentStringTag;
|
2000-02-08 15:53:34 +03:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMElement> parentElement = do_QueryInterface(aParent);
|
|
|
|
if (!parentElement) return PR_FALSE;
|
|
|
|
|
|
|
|
parentElement->GetTagName(parentStringTag);
|
2000-02-08 15:53:34 +03:00
|
|
|
return TagCanContainTag(parentStringTag, aChildTag);
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
nsEditor::TagCanContain(const nsString &aParentTag, nsIDOMNode* aChild)
|
|
|
|
{
|
|
|
|
nsAutoString childStringTag;
|
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
if (IsTextNode(aChild))
|
|
|
|
{
|
2000-04-18 11:44:58 +04:00
|
|
|
childStringTag.AssignWithConversion("__moz_text");
|
2000-03-24 03:26:47 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMElement> childElement = do_QueryInterface(aChild);
|
|
|
|
if (!childElement) return PR_FALSE;
|
|
|
|
childElement->GetTagName(childStringTag);
|
|
|
|
}
|
2000-02-08 15:53:34 +03:00
|
|
|
return TagCanContainTag(aParentTag, childStringTag);
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
nsEditor::TagCanContainTag(const nsString &aParentTag, const nsString &aChildTag)
|
|
|
|
{
|
|
|
|
// if we don't have a dtd then assume we can insert whatever want
|
|
|
|
if (!mDTD) return PR_TRUE;
|
|
|
|
|
|
|
|
PRInt32 childTagEnum, parentTagEnum;
|
|
|
|
nsAutoString non_const_childTag(aChildTag);
|
|
|
|
nsAutoString non_const_parentTag(aParentTag);
|
|
|
|
nsresult res = mDTD->StringTagToIntTag(non_const_childTag,&childTagEnum);
|
|
|
|
if (NS_FAILED(res)) return PR_FALSE;
|
|
|
|
res = mDTD->StringTagToIntTag(non_const_parentTag,&parentTagEnum);
|
1999-08-09 05:37:50 +04:00
|
|
|
if (NS_FAILED(res)) return PR_FALSE;
|
|
|
|
|
|
|
|
return mDTD->CanContain(parentTagEnum, childTagEnum);
|
|
|
|
}
|
|
|
|
|
2000-05-10 01:06:49 +04:00
|
|
|
PRBool
|
|
|
|
nsEditor::IsDescendantOfBody(nsIDOMNode *inNode)
|
|
|
|
{
|
|
|
|
if (!inNode) return PR_FALSE;
|
|
|
|
nsCOMPtr<nsIDOMElement> junk;
|
|
|
|
if (!mBodyElement) GetRootElement(getter_AddRefs(junk));
|
|
|
|
if (!mBodyElement) return PR_FALSE;
|
|
|
|
nsCOMPtr<nsIDOMNode> root = do_QueryInterface(mBodyElement);
|
|
|
|
|
|
|
|
if (inNode == root.get()) return PR_TRUE;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNode> parent, node = do_QueryInterface(inNode);
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
node->GetParentNode(getter_AddRefs(parent));
|
|
|
|
if (parent == root) return PR_TRUE;
|
|
|
|
node = parent;
|
|
|
|
} while (parent);
|
|
|
|
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
1999-09-30 00:08:15 +04:00
|
|
|
PRBool
|
|
|
|
nsEditor::IsContainer(nsIDOMNode *aNode)
|
|
|
|
{
|
|
|
|
if (!aNode) return PR_FALSE;
|
|
|
|
nsAutoString stringTag;
|
|
|
|
PRInt32 tagEnum;
|
|
|
|
nsresult res = aNode->GetNodeName(stringTag);
|
|
|
|
if (NS_FAILED(res)) return PR_FALSE;
|
|
|
|
res = mDTD->StringTagToIntTag(stringTag,&tagEnum);
|
|
|
|
if (NS_FAILED(res)) return PR_FALSE;
|
|
|
|
return mDTD->IsContainer(tagEnum);
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
PRBool
|
|
|
|
nsEditor::IsEditable(nsIDOMNode *aNode)
|
|
|
|
{
|
|
|
|
if (!aNode) return PR_FALSE;
|
|
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
|
|
GetPresShell(getter_AddRefs(shell));
|
|
|
|
if (!shell) return PR_FALSE;
|
2000-01-19 02:45:35 +03:00
|
|
|
|
|
|
|
if (IsMozEditorBogusNode(aNode)) return PR_FALSE;
|
1999-09-23 04:10:51 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
// it's not the bogus node, so see if it is an irrelevant text node
|
|
|
|
if (PR_TRUE==IsTextNode(aNode))
|
|
|
|
{
|
1999-09-22 05:23:58 +04:00
|
|
|
nsCOMPtr<nsIDOMCharacterData> text = do_QueryInterface(aNode);
|
|
|
|
// nsCOMPtr<nsIDOMComment> commentNode = do_QueryInterface(aNode);
|
1999-08-09 05:37:50 +04:00
|
|
|
if (text)
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsAutoString data;
|
|
|
|
text->GetData(data);
|
|
|
|
PRUint32 length = data.Length();
|
|
|
|
if (0==length) {
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
// if the node contains only newlines, it's not editable
|
1999-09-22 05:23:58 +04:00
|
|
|
// you could use nsITextContent::IsOnlyWhitespace here
|
1999-08-09 05:37:50 +04:00
|
|
|
PRUint32 i;
|
|
|
|
for (i=0; i<length; i++)
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
1999-09-23 04:10:51 +04:00
|
|
|
PRUnichar character = data.CharAt(i);
|
1999-09-23 23:08:42 +04:00
|
|
|
if ('\n'!=character) {
|
1999-08-09 05:37:50 +04:00
|
|
|
return PR_TRUE;
|
1999-05-05 08:05:19 +04:00
|
|
|
}
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
return PR_FALSE;
|
1999-05-05 08:05:19 +04:00
|
|
|
}
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
// we got this far, so see if it has a frame. If so, we'll edit it.
|
|
|
|
nsIFrame *resultFrame;
|
|
|
|
nsCOMPtr<nsIContent>content;
|
|
|
|
content = do_QueryInterface(aNode);
|
|
|
|
if (content)
|
|
|
|
{
|
|
|
|
nsresult result = shell->GetPrimaryFrameFor(content, &resultFrame);
|
|
|
|
if (NS_FAILED(result) || !resultFrame) { // if it has no frame, it is not editable
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
1999-09-30 00:08:15 +04:00
|
|
|
else {
|
|
|
|
// it has a frame, so it might editable
|
|
|
|
// but not if it's a formatting whitespace node
|
|
|
|
if (IsEmptyTextContent(content)) return PR_FALSE;
|
1999-08-09 05:37:50 +04:00
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return PR_FALSE; // it's not a content object (???) so it's not editable
|
1999-05-05 08:05:19 +04:00
|
|
|
}
|
|
|
|
|
2000-01-19 02:45:35 +03:00
|
|
|
PRBool
|
|
|
|
nsEditor::IsMozEditorBogusNode(nsIDOMNode *aNode)
|
|
|
|
{
|
|
|
|
if (!aNode)
|
|
|
|
return PR_FALSE;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMElement>element;
|
|
|
|
element = do_QueryInterface(aNode);
|
|
|
|
if (element)
|
|
|
|
{
|
2000-04-18 11:44:58 +04:00
|
|
|
nsAutoString att; att.AssignWithConversion(kMOZEditorBogusNodeAttr);
|
2000-01-19 02:45:35 +03:00
|
|
|
nsAutoString val;
|
|
|
|
(void)element->GetAttribute(att, val);
|
2000-04-18 11:44:58 +04:00
|
|
|
if (val.EqualsWithConversion(kMOZEditorBogusNodeValue)) {
|
2000-01-19 02:45:35 +03:00
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
1999-09-30 00:08:15 +04:00
|
|
|
PRBool
|
|
|
|
nsEditor::IsEmptyTextContent(nsIContent* aContent)
|
|
|
|
{
|
|
|
|
PRBool result = PR_FALSE;
|
|
|
|
nsCOMPtr<nsITextContent> tc(do_QueryInterface(aContent));
|
|
|
|
if (tc) {
|
|
|
|
tc->IsOnlyWhitespace(&result);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-05-05 08:05:19 +04:00
|
|
|
nsresult
|
1999-08-09 05:37:50 +04:00
|
|
|
nsEditor::CountEditableChildren(nsIDOMNode *aNode, PRUint32 &outCount)
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
outCount = 0;
|
|
|
|
if (!aNode) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
nsresult res=NS_OK;
|
|
|
|
PRBool hasChildNodes;
|
|
|
|
aNode->HasChildNodes(&hasChildNodes);
|
|
|
|
if (PR_TRUE==hasChildNodes)
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMNodeList>nodeList;
|
|
|
|
PRUint32 len;
|
|
|
|
PRUint32 i;
|
|
|
|
res = aNode->GetChildNodes(getter_AddRefs(nodeList));
|
|
|
|
if (NS_SUCCEEDED(res) && nodeList)
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nodeList->GetLength(&len);
|
|
|
|
for (i=0 ; i<len; i++)
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> child;
|
|
|
|
res = nodeList->Item((PRInt32)i, getter_AddRefs(child));
|
|
|
|
if ((NS_SUCCEEDED(res)) && (child))
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
if (IsEditable(child))
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
outCount++;
|
1999-05-05 08:05:19 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
else if (!nodeList)
|
|
|
|
res = NS_ERROR_NULL_POINTER;
|
1999-05-05 08:05:19 +04:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
return res;
|
1999-05-05 08:05:19 +04:00
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
//END nsEditor static utility methods
|
1999-05-05 08:05:19 +04:00
|
|
|
|
1999-05-05 08:51:54 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP nsEditor::IncDocModCount(PRInt32 inNumMods)
|
|
|
|
{
|
1999-08-25 14:51:55 +04:00
|
|
|
if (!mDocWeak) return NS_ERROR_NOT_INITIALIZED;
|
1999-05-05 08:05:19 +04:00
|
|
|
|
1999-08-25 14:51:55 +04:00
|
|
|
nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(mDocWeak);
|
|
|
|
if (!doc) return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
nsCOMPtr<nsIDiskDocument> diskDoc = do_QueryInterface(doc);
|
1999-08-09 05:37:50 +04:00
|
|
|
if (diskDoc)
|
|
|
|
diskDoc->IncrementModCount(inNumMods);
|
1999-05-05 08:05:19 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NotifyDocumentListeners(eDocumentStateChanged);
|
|
|
|
return NS_OK;
|
1999-05-13 02:24:47 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsEditor::GetDocModCount(PRInt32 &outModCount)
|
|
|
|
{
|
1999-08-25 14:51:55 +04:00
|
|
|
if (!mDocWeak) return NS_ERROR_NOT_INITIALIZED;
|
1999-05-13 02:24:47 +04:00
|
|
|
|
1999-08-25 14:51:55 +04:00
|
|
|
nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(mDocWeak);
|
|
|
|
if (!doc) return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
nsCOMPtr<nsIDiskDocument> diskDoc = do_QueryInterface(doc);
|
1999-08-03 04:58:38 +04:00
|
|
|
if (diskDoc)
|
|
|
|
diskDoc->GetModCount(&outModCount);
|
1999-05-13 02:24:47 +04:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsEditor::ResetDocModCount()
|
|
|
|
{
|
1999-08-25 14:51:55 +04:00
|
|
|
if (!mDocWeak) return NS_ERROR_NOT_INITIALIZED;
|
1999-05-13 02:24:47 +04:00
|
|
|
|
1999-08-25 14:51:55 +04:00
|
|
|
nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(mDocWeak);
|
|
|
|
if (!doc) return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
nsCOMPtr<nsIDiskDocument> diskDoc = do_QueryInterface(doc);
|
1999-08-03 04:58:38 +04:00
|
|
|
if (diskDoc)
|
1999-05-13 02:24:47 +04:00
|
|
|
diskDoc->ResetModCount();
|
|
|
|
|
1999-08-03 04:58:38 +04:00
|
|
|
NotifyDocumentListeners(eDocumentStateChanged);
|
1999-05-13 02:24:47 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-05-05 08:05:19 +04:00
|
|
|
|
|
|
|
void nsEditor::HACKForceRedraw()
|
|
|
|
{
|
|
|
|
#ifdef HACK_FORCE_REDRAW
|
|
|
|
// 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/11/99
|
|
|
|
// BEGIN HACK!!!
|
|
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
|
|
|
1999-08-03 04:58:38 +04:00
|
|
|
GetPresShell(getter_AddRefs(shell));
|
1999-05-05 08:05:19 +04:00
|
|
|
if (shell) {
|
|
|
|
nsCOMPtr<nsIViewManager> viewmgr;
|
|
|
|
|
|
|
|
shell->GetViewManager(getter_AddRefs(viewmgr));
|
|
|
|
if (viewmgr) {
|
|
|
|
nsIView* view;
|
1999-08-03 04:58:38 +04:00
|
|
|
viewmgr->GetRootView(view); // views are not refCounted
|
1999-05-05 08:05:19 +04:00
|
|
|
if (view) {
|
1999-11-14 05:51:25 +03:00
|
|
|
viewmgr->UpdateView(view,NS_VMREFRESH_IMMEDIATE);
|
1999-05-05 08:05:19 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// END HACK
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsresult
|
|
|
|
nsEditor::GetFirstNodeOfType(nsIDOMNode *aStartNode,
|
|
|
|
const nsString &aTag,
|
|
|
|
nsIDOMNode **aResult)
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
|
|
|
nsresult result=NS_OK;
|
1999-02-12 20:18:58 +03:00
|
|
|
|
1999-05-05 08:05:19 +04:00
|
|
|
if (!aStartNode)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
if (!aResult)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMElement> element;
|
|
|
|
*aResult = nsnull;
|
|
|
|
nsCOMPtr<nsIDOMNode> childNode;
|
|
|
|
result = aStartNode->GetFirstChild(getter_AddRefs(childNode));
|
|
|
|
while (childNode)
|
|
|
|
{
|
2000-01-11 23:49:15 +03:00
|
|
|
result = childNode->QueryInterface(NS_GET_IID(nsIDOMNode),getter_AddRefs(element));
|
1999-05-05 08:05:19 +04:00
|
|
|
nsAutoString tag;
|
|
|
|
if (NS_SUCCEEDED(result) && (element))
|
|
|
|
{
|
|
|
|
element->GetTagName(tag);
|
1999-06-08 10:04:51 +04:00
|
|
|
if (PR_TRUE==aTag.EqualsIgnoreCase(tag))
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
2000-01-11 23:49:15 +03:00
|
|
|
return (childNode->QueryInterface(NS_GET_IID(nsIDOMNode),(void **) aResult)); // does the addref
|
1999-05-05 08:05:19 +04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
1999-05-06 03:27:17 +04:00
|
|
|
result = GetFirstNodeOfType(childNode, aTag, aResult);
|
1999-05-05 08:05:19 +04:00
|
|
|
if (nsnull!=*aResult)
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nsCOMPtr<nsIDOMNode> temp = childNode;
|
|
|
|
temp->GetNextSibling(getter_AddRefs(childNode));
|
|
|
|
}
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
1999-03-02 08:30:53 +03:00
|
|
|
|
1999-05-05 08:05:19 +04:00
|
|
|
nsresult
|
|
|
|
nsEditor::GetFirstTextNode(nsIDOMNode *aNode, nsIDOMNode **aRetNode)
|
1999-02-12 20:18:58 +03:00
|
|
|
{
|
1999-05-05 08:05:19 +04:00
|
|
|
if (!aNode || !aRetNode)
|
|
|
|
{
|
|
|
|
NS_NOTREACHED("GetFirstTextNode Failed");
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRUint16 mType;
|
|
|
|
PRBool mCNodes;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNode> answer;
|
|
|
|
|
|
|
|
aNode->GetNodeType(&mType);
|
|
|
|
|
|
|
|
if (nsIDOMNode::ELEMENT_NODE == mType) {
|
|
|
|
if (NS_SUCCEEDED(aNode->HasChildNodes(&mCNodes)) && PR_TRUE == mCNodes)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> node1;
|
|
|
|
nsCOMPtr<nsIDOMNode> node2;
|
|
|
|
|
|
|
|
if (!NS_SUCCEEDED(aNode->GetFirstChild(getter_AddRefs(node1))))
|
|
|
|
{
|
|
|
|
NS_NOTREACHED("GetFirstTextNode Failed");
|
|
|
|
}
|
|
|
|
while(!answer && node1)
|
|
|
|
{
|
|
|
|
GetFirstTextNode(node1, getter_AddRefs(answer));
|
|
|
|
node1->GetNextSibling(getter_AddRefs(node2));
|
|
|
|
node1 = node2;
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1999-05-05 08:05:19 +04:00
|
|
|
else if (nsIDOMNode::TEXT_NODE == mType) {
|
|
|
|
answer = do_QueryInterface(aNode);
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
1999-05-05 08:05:19 +04:00
|
|
|
|
|
|
|
// OK, now return the answer, if any
|
|
|
|
*aRetNode = answer;
|
|
|
|
if (*aRetNode)
|
|
|
|
NS_IF_ADDREF(*aRetNode);
|
|
|
|
else
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
return NS_OK;
|
1999-02-12 20:18:58 +03:00
|
|
|
}
|
|
|
|
|
1999-05-05 08:05:19 +04:00
|
|
|
|
|
|
|
//END nsEditor Private methods
|
1999-02-12 20:18:58 +03:00
|
|
|
|
1999-08-25 14:51:55 +04:00
|
|
|
|
|
|
|
|
1999-05-17 16:22:31 +04:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// GetTag: digs out the atom for the tag of this node
|
|
|
|
//
|
|
|
|
nsCOMPtr<nsIAtom>
|
|
|
|
nsEditor::GetTag(nsIDOMNode *aNode)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIAtom> atom;
|
|
|
|
|
|
|
|
if (!aNode)
|
|
|
|
{
|
|
|
|
NS_NOTREACHED("null node passed to nsEditor::GetTag()");
|
|
|
|
return atom;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
|
1999-08-18 12:13:06 +04:00
|
|
|
if (content)
|
|
|
|
content->GetTag(*getter_AddRefs(atom));
|
1999-05-17 16:22:31 +04:00
|
|
|
|
|
|
|
return atom;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-05-29 01:17:30 +04:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// GetTagString: digs out string for the tag of this node
|
|
|
|
//
|
|
|
|
nsresult
|
|
|
|
nsEditor::GetTagString(nsIDOMNode *aNode, nsString& outString)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIAtom> atom;
|
|
|
|
|
|
|
|
if (!aNode)
|
|
|
|
{
|
|
|
|
NS_NOTREACHED("null node passed to nsEditor::GetTag()");
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
}
|
|
|
|
|
|
|
|
atom = GetTag(aNode);
|
|
|
|
if (atom)
|
|
|
|
{
|
|
|
|
atom->ToString(outString);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-05-17 16:22:31 +04:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// NodesSameType: do these nodes have the same tag?
|
|
|
|
//
|
|
|
|
PRBool
|
|
|
|
nsEditor::NodesSameType(nsIDOMNode *aNode1, nsIDOMNode *aNode2)
|
|
|
|
{
|
|
|
|
if (!aNode1 || !aNode2)
|
|
|
|
{
|
|
|
|
NS_NOTREACHED("null node passed to nsEditor::NodesSameType()");
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIAtom> atom1 = GetTag(aNode1);
|
|
|
|
nsCOMPtr<nsIAtom> atom2 = GetTag(aNode2);
|
|
|
|
|
|
|
|
if (atom1.get() == atom2.get())
|
|
|
|
return PR_TRUE;
|
|
|
|
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// IsBlockNode: true if this node is an html block node
|
|
|
|
//
|
|
|
|
PRBool
|
|
|
|
nsEditor::IsBlockNode(nsIDOMNode *aNode)
|
|
|
|
{
|
1999-05-29 01:17:30 +04:00
|
|
|
return !IsInlineNode(aNode);
|
1999-05-17 16:22:31 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// IsInlineNode: true if this node is an html inline node
|
|
|
|
//
|
|
|
|
PRBool
|
|
|
|
nsEditor::IsInlineNode(nsIDOMNode *aNode)
|
|
|
|
{
|
1999-05-29 01:17:30 +04:00
|
|
|
PRBool retVal = PR_FALSE;
|
|
|
|
IsNodeInline(aNode, retVal);
|
|
|
|
return retVal;
|
1999-05-17 16:22:31 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// GetBlockNodeParent: returns enclosing block level ancestor, if any
|
|
|
|
//
|
|
|
|
nsCOMPtr<nsIDOMNode>
|
|
|
|
nsEditor::GetBlockNodeParent(nsIDOMNode *aNode)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> tmp;
|
|
|
|
nsCOMPtr<nsIDOMNode> p;
|
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
if (!aNode)
|
|
|
|
{
|
|
|
|
NS_NOTREACHED("null node passed to GetBlockNodeParent()");
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
1999-05-17 16:22:31 +04:00
|
|
|
if (NS_FAILED(aNode->GetParentNode(getter_AddRefs(p)))) // no parent, ran off top of tree
|
|
|
|
return tmp;
|
|
|
|
|
|
|
|
while (p && !IsBlockNode(p))
|
|
|
|
{
|
1999-09-10 02:22:14 +04:00
|
|
|
if ( NS_FAILED(p->GetParentNode(getter_AddRefs(tmp))) || !tmp) // no parent, ran off top of tree
|
1999-05-17 16:22:31 +04:00
|
|
|
return p;
|
|
|
|
|
|
|
|
p = tmp;
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// HasSameBlockNodeParent: true if nodes have same block level ancestor
|
|
|
|
//
|
|
|
|
PRBool
|
|
|
|
nsEditor::HasSameBlockNodeParent(nsIDOMNode *aNode1, nsIDOMNode *aNode2)
|
|
|
|
{
|
|
|
|
if (!aNode1 || !aNode2)
|
|
|
|
{
|
|
|
|
NS_NOTREACHED("null node passed to HasSameBlockNodeParent()");
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aNode1 == aNode2)
|
|
|
|
return PR_TRUE;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNode> p1 = GetBlockNodeParent(aNode1);
|
|
|
|
nsCOMPtr<nsIDOMNode> p2 = GetBlockNodeParent(aNode2);
|
|
|
|
|
|
|
|
return (p1 == p2);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// GetBlockSection: return leftmost/rightmost nodes in aChild's block
|
|
|
|
//
|
|
|
|
nsresult
|
|
|
|
nsEditor::GetBlockSection(nsIDOMNode *aChild,
|
|
|
|
nsIDOMNode **aLeftNode,
|
|
|
|
nsIDOMNode **aRightNode)
|
|
|
|
{
|
|
|
|
nsresult result = NS_OK;
|
|
|
|
if (!aChild || !aLeftNode || !aRightNode) {return NS_ERROR_NULL_POINTER;}
|
|
|
|
*aLeftNode = aChild;
|
|
|
|
*aRightNode = aChild;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNode>sibling;
|
|
|
|
result = aChild->GetPreviousSibling(getter_AddRefs(sibling));
|
|
|
|
while ((NS_SUCCEEDED(result)) && sibling)
|
|
|
|
{
|
|
|
|
PRBool isInline;
|
|
|
|
IsNodeInline(sibling, isInline);
|
|
|
|
if (PR_FALSE==isInline)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMCharacterData>nodeAsText = do_QueryInterface(sibling);
|
|
|
|
if (!nodeAsText) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// XXX: needs some logic to work for other leaf nodes besides text!
|
|
|
|
}
|
|
|
|
*aLeftNode = sibling;
|
|
|
|
result = (*aLeftNode)->GetPreviousSibling(getter_AddRefs(sibling));
|
|
|
|
}
|
|
|
|
NS_ADDREF((*aLeftNode));
|
|
|
|
// now do the right side
|
|
|
|
result = aChild->GetNextSibling(getter_AddRefs(sibling));
|
|
|
|
while ((NS_SUCCEEDED(result)) && sibling)
|
|
|
|
{
|
|
|
|
PRBool isInline;
|
|
|
|
IsNodeInline(sibling, isInline);
|
|
|
|
if (PR_FALSE==isInline)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMCharacterData>nodeAsText = do_QueryInterface(sibling);
|
|
|
|
if (!nodeAsText) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*aRightNode = sibling;
|
|
|
|
result = (*aRightNode)->GetNextSibling(getter_AddRefs(sibling));
|
|
|
|
}
|
|
|
|
NS_ADDREF((*aRightNode));
|
|
|
|
if (gNoisy) { printf("GetBlockSection returning %p %p\n",
|
|
|
|
(*aLeftNode), (*aRightNode)); }
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// GetBlockSectionsForRange: return list of block sections that intersect
|
|
|
|
// this range
|
|
|
|
nsresult
|
|
|
|
nsEditor::GetBlockSectionsForRange(nsIDOMRange *aRange, nsISupportsArray *aSections)
|
|
|
|
{
|
|
|
|
if (!aRange || !aSections) {return NS_ERROR_NULL_POINTER;}
|
|
|
|
|
|
|
|
nsresult result;
|
|
|
|
nsCOMPtr<nsIContentIterator>iter;
|
|
|
|
result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull,
|
|
|
|
NS_GET_IID(nsIContentIterator), getter_AddRefs(iter));
|
|
|
|
if ((NS_SUCCEEDED(result)) && iter)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMRange> lastRange;
|
|
|
|
iter->Init(aRange);
|
|
|
|
nsCOMPtr<nsIContent> currentContent;
|
|
|
|
iter->CurrentNode(getter_AddRefs(currentContent));
|
|
|
|
while (NS_ENUMERATOR_FALSE == iter->IsDone())
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode>currentNode = do_QueryInterface(currentContent);
|
|
|
|
if (currentNode)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIAtom> currentContentTag;
|
|
|
|
currentContent->GetTag(*getter_AddRefs(currentContentTag));
|
|
|
|
// <BR> divides block content ranges. We can achieve this by nulling out lastRange
|
|
|
|
if (nsIEditProperty::br==currentContentTag.get())
|
|
|
|
{
|
|
|
|
lastRange = do_QueryInterface(nsnull);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
PRBool isInlineOrText;
|
|
|
|
result = IsNodeInline(currentNode, isInlineOrText);
|
|
|
|
if (PR_FALSE==isInlineOrText)
|
|
|
|
{
|
|
|
|
PRUint16 nodeType;
|
|
|
|
currentNode->GetNodeType(&nodeType);
|
|
|
|
if (nsIDOMNode::TEXT_NODE == nodeType) {
|
|
|
|
isInlineOrText = PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (PR_TRUE==isInlineOrText)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode>leftNode;
|
|
|
|
nsCOMPtr<nsIDOMNode>rightNode;
|
|
|
|
result = GetBlockSection(currentNode,
|
|
|
|
getter_AddRefs(leftNode),
|
|
|
|
getter_AddRefs(rightNode));
|
|
|
|
if (gNoisy) {printf("currentNode %p has block content (%p,%p)\n", currentNode.get(), leftNode.get(), rightNode.get());}
|
|
|
|
if ((NS_SUCCEEDED(result)) && leftNode && rightNode)
|
|
|
|
{
|
|
|
|
// add range to the list if it doesn't overlap with the previous range
|
|
|
|
PRBool addRange=PR_TRUE;
|
|
|
|
if (lastRange)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> lastStartNode;
|
|
|
|
nsCOMPtr<nsIDOMElement> blockParentOfLastStartNode;
|
|
|
|
lastRange->GetStartParent(getter_AddRefs(lastStartNode));
|
|
|
|
blockParentOfLastStartNode = do_QueryInterface(GetBlockNodeParent(lastStartNode));
|
|
|
|
if (blockParentOfLastStartNode)
|
|
|
|
{
|
|
|
|
if (gNoisy) {printf("lastStartNode %p has block parent %p\n", lastStartNode.get(), blockParentOfLastStartNode.get());}
|
|
|
|
nsCOMPtr<nsIDOMElement> blockParentOfLeftNode;
|
|
|
|
blockParentOfLeftNode = do_QueryInterface(GetBlockNodeParent(leftNode));
|
|
|
|
if (blockParentOfLeftNode)
|
|
|
|
{
|
|
|
|
if (gNoisy) {printf("leftNode %p has block parent %p\n", leftNode.get(), blockParentOfLeftNode.get());}
|
|
|
|
if (blockParentOfLastStartNode==blockParentOfLeftNode) {
|
|
|
|
addRange = PR_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (PR_TRUE==addRange)
|
|
|
|
{
|
|
|
|
if (gNoisy) {printf("adding range, setting lastRange with start node %p\n", leftNode.get());}
|
|
|
|
nsCOMPtr<nsIDOMRange> range;
|
|
|
|
result = nsComponentManager::CreateInstance(kCRangeCID, nsnull,
|
|
|
|
NS_GET_IID(nsIDOMRange), getter_AddRefs(range));
|
|
|
|
if ((NS_SUCCEEDED(result)) && range)
|
|
|
|
{ // initialize the range
|
|
|
|
range->SetStart(leftNode, 0);
|
|
|
|
range->SetEnd(rightNode, 0);
|
|
|
|
aSections->AppendElement(range);
|
|
|
|
lastRange = do_QueryInterface(range);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* do not check result here, and especially do not return the result code.
|
|
|
|
* we rely on iter->IsDone to tell us when the iteration is complete
|
|
|
|
*/
|
|
|
|
iter->Next();
|
|
|
|
iter->CurrentNode(getter_AddRefs(currentContent));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-05-17 16:22:31 +04:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// IsTextOrElementNode: true if node of dom type element or text
|
|
|
|
//
|
|
|
|
PRBool
|
|
|
|
nsEditor::IsTextOrElementNode(nsIDOMNode *aNode)
|
|
|
|
{
|
|
|
|
if (!aNode)
|
|
|
|
{
|
|
|
|
NS_NOTREACHED("null node passed to IsTextOrElementNode()");
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRUint16 nodeType;
|
|
|
|
aNode->GetNodeType(&nodeType);
|
|
|
|
if ((nodeType == nsIDOMNode::ELEMENT_NODE) || (nodeType == nsIDOMNode::TEXT_NODE))
|
|
|
|
return PR_TRUE;
|
|
|
|
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// IsTextNode: true if node of dom type text
|
|
|
|
//
|
|
|
|
PRBool
|
|
|
|
nsEditor::IsTextNode(nsIDOMNode *aNode)
|
|
|
|
{
|
|
|
|
if (!aNode)
|
|
|
|
{
|
|
|
|
NS_NOTREACHED("null node passed to IsTextNode()");
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRUint16 nodeType;
|
|
|
|
aNode->GetNodeType(&nodeType);
|
|
|
|
if (nodeType == nsIDOMNode::TEXT_NODE)
|
|
|
|
return PR_TRUE;
|
|
|
|
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// GetIndexOf: returns the position index of the node in the parent
|
|
|
|
//
|
|
|
|
PRInt32
|
|
|
|
nsEditor::GetIndexOf(nsIDOMNode *parent, nsIDOMNode *child)
|
|
|
|
{
|
1999-06-11 01:31:42 +04:00
|
|
|
PRInt32 idx = 0;
|
1999-05-17 16:22:31 +04:00
|
|
|
|
|
|
|
NS_PRECONDITION(parent, "null parent passed to nsEditor::GetIndexOf");
|
|
|
|
NS_PRECONDITION(parent, "null child passed to nsEditor::GetIndexOf");
|
|
|
|
nsCOMPtr<nsIContent> content = do_QueryInterface(parent);
|
|
|
|
nsCOMPtr<nsIContent> cChild = do_QueryInterface(child);
|
|
|
|
NS_PRECONDITION(content, "null content in nsEditor::GetIndexOf");
|
|
|
|
NS_PRECONDITION(cChild, "null content in nsEditor::GetIndexOf");
|
|
|
|
|
1999-06-11 01:31:42 +04:00
|
|
|
nsresult res = content->IndexOf(cChild, idx);
|
1999-05-17 16:22:31 +04:00
|
|
|
if (NS_FAILED(res))
|
|
|
|
{
|
|
|
|
NS_NOTREACHED("could not find child in parent - nsEditor::GetIndexOf");
|
|
|
|
}
|
1999-06-11 01:31:42 +04:00
|
|
|
return idx;
|
1999-05-17 16:22:31 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// GetChildAt: returns the node at this position index in the parent
|
|
|
|
//
|
|
|
|
nsCOMPtr<nsIDOMNode>
|
|
|
|
nsEditor::GetChildAt(nsIDOMNode *aParent, PRInt32 aOffset)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> resultNode;
|
|
|
|
|
|
|
|
if (!aParent)
|
|
|
|
return resultNode;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIContent> content = do_QueryInterface(aParent);
|
|
|
|
nsCOMPtr<nsIContent> cChild;
|
|
|
|
NS_PRECONDITION(content, "null content in nsEditor::GetChildAt");
|
|
|
|
|
|
|
|
if (NS_FAILED(content->ChildAt(aOffset, *getter_AddRefs(cChild))))
|
|
|
|
return resultNode;
|
|
|
|
|
|
|
|
resultNode = do_QueryInterface(cChild);
|
|
|
|
return resultNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// NextNodeInBlock: gets the next/prev node in the block, if any. Next node
|
|
|
|
// must be an element or text node, others are ignored
|
|
|
|
nsCOMPtr<nsIDOMNode>
|
|
|
|
nsEditor::NextNodeInBlock(nsIDOMNode *aNode, IterDirection aDir)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> nullNode;
|
|
|
|
nsCOMPtr<nsIContent> content;
|
|
|
|
nsCOMPtr<nsIContent> blockContent;
|
|
|
|
nsCOMPtr<nsIDOMNode> node;
|
|
|
|
nsCOMPtr<nsIDOMNode> blockParent;
|
|
|
|
|
|
|
|
if (!aNode) return nullNode;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIContentIterator> iter;
|
|
|
|
if (NS_FAILED(nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull,
|
2000-01-11 23:49:15 +03:00
|
|
|
NS_GET_IID(nsIContentIterator),
|
1999-05-17 16:22:31 +04:00
|
|
|
getter_AddRefs(iter))))
|
|
|
|
return nullNode;
|
|
|
|
|
|
|
|
// much gnashing of teeth as we twit back and forth between content and domnode types
|
|
|
|
content = do_QueryInterface(aNode);
|
|
|
|
if (IsBlockNode(aNode))
|
|
|
|
{
|
|
|
|
blockParent = do_QueryInterface(aNode);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
blockParent = GetBlockNodeParent(aNode);
|
|
|
|
}
|
|
|
|
if (!blockParent) return nullNode;
|
|
|
|
blockContent = do_QueryInterface(blockParent);
|
|
|
|
if (!blockContent) return nullNode;
|
|
|
|
|
|
|
|
if (NS_FAILED(iter->Init(blockContent))) return nullNode;
|
|
|
|
if (NS_FAILED(iter->PositionAt(content))) return nullNode;
|
|
|
|
|
1999-10-28 07:16:48 +04:00
|
|
|
while (NS_ENUMERATOR_FALSE == iter->IsDone())
|
1999-05-17 16:22:31 +04:00
|
|
|
{
|
1999-08-25 14:51:55 +04:00
|
|
|
if (NS_FAILED(iter->CurrentNode(getter_AddRefs(content)))) return nullNode;
|
1999-05-17 16:22:31 +04:00
|
|
|
// ignore nodes that aren't elements or text, or that are the block parent
|
|
|
|
node = do_QueryInterface(content);
|
1999-08-18 12:13:06 +04:00
|
|
|
if (node && IsTextOrElementNode(node) && (node != blockParent) && (node.get() != aNode))
|
1999-05-17 16:22:31 +04:00
|
|
|
return node;
|
|
|
|
|
|
|
|
if (aDir == kIterForward)
|
|
|
|
iter->Next();
|
|
|
|
else
|
|
|
|
iter->Prev();
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// GetStartNodeAndOffset: returns whatever the start parent & offset is of
|
|
|
|
// the first range in the selection.
|
|
|
|
nsresult
|
|
|
|
nsEditor::GetStartNodeAndOffset(nsIDOMSelection *aSelection,
|
|
|
|
nsCOMPtr<nsIDOMNode> *outStartNode,
|
|
|
|
PRInt32 *outStartOffset)
|
|
|
|
{
|
1999-07-15 22:19:20 +04:00
|
|
|
if (!outStartNode || !outStartOffset || !aSelection)
|
1999-05-17 16:22:31 +04:00
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIEnumerator> enumerator;
|
1999-07-15 22:19:20 +04:00
|
|
|
nsresult result;
|
1999-07-18 06:27:19 +04:00
|
|
|
result = aSelection->GetEnumerator(getter_AddRefs(enumerator));
|
1999-07-15 22:19:20 +04:00
|
|
|
if (NS_FAILED(result) || !enumerator)
|
1999-05-17 16:22:31 +04:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
enumerator->First();
|
1999-07-25 22:14:44 +04:00
|
|
|
nsCOMPtr<nsISupports> currentItem;
|
|
|
|
if ((NS_FAILED(enumerator->CurrentItem(getter_AddRefs(currentItem)))) || !currentItem)
|
1999-05-17 16:22:31 +04:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
|
|
|
|
if (!range)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
if (NS_FAILED(range->GetStartParent(getter_AddRefs(*outStartNode))))
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
if (NS_FAILED(range->GetStartOffset(outStartOffset)))
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// GetEndNodeAndOffset: returns whatever the end parent & offset is of
|
|
|
|
// the first range in the selection.
|
|
|
|
nsresult
|
|
|
|
nsEditor::GetEndNodeAndOffset(nsIDOMSelection *aSelection,
|
|
|
|
nsCOMPtr<nsIDOMNode> *outEndNode,
|
|
|
|
PRInt32 *outEndOffset)
|
|
|
|
{
|
|
|
|
if (!outEndNode || !outEndOffset)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIEnumerator> enumerator;
|
1999-07-18 06:27:19 +04:00
|
|
|
nsresult result = aSelection->GetEnumerator(getter_AddRefs(enumerator));
|
1999-07-15 23:13:46 +04:00
|
|
|
if (NS_FAILED(result) || !enumerator)
|
1999-05-17 16:22:31 +04:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
enumerator->First();
|
1999-07-25 22:14:44 +04:00
|
|
|
nsCOMPtr<nsISupports> currentItem;
|
|
|
|
if ((NS_FAILED(enumerator->CurrentItem(getter_AddRefs(currentItem)))) || !currentItem)
|
1999-05-17 16:22:31 +04:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
|
|
|
|
if (!range)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
if (NS_FAILED(range->GetEndParent(getter_AddRefs(*outEndNode))))
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
if (NS_FAILED(range->GetEndOffset(outEndOffset)))
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// IsPreformatted: checks the style info for the node for the preformatted
|
|
|
|
// text style.
|
|
|
|
nsresult
|
|
|
|
nsEditor::IsPreformatted(nsIDOMNode *aNode, PRBool *aResult)
|
|
|
|
{
|
|
|
|
nsresult result;
|
|
|
|
nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
|
|
|
|
nsIFrame *frame;
|
|
|
|
nsCOMPtr<nsIStyleContext> styleContext;
|
|
|
|
const nsStyleText* styleText;
|
|
|
|
PRBool bPreformatted;
|
|
|
|
|
|
|
|
if (!aResult || !content) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
1999-08-25 14:51:55 +04:00
|
|
|
if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
|
|
|
|
if (!ps) return NS_ERROR_NOT_INITIALIZED;
|
1999-05-17 16:22:31 +04:00
|
|
|
|
1999-08-25 14:51:55 +04:00
|
|
|
result = ps->GetPrimaryFrameFor(content, &frame);
|
1999-05-17 16:22:31 +04:00
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
|
1999-08-25 14:51:55 +04:00
|
|
|
result = ps->GetStyleContextFor(frame, getter_AddRefs(styleContext));
|
2000-03-25 05:27:57 +03:00
|
|
|
if (NS_FAILED(result))
|
|
|
|
{
|
|
|
|
// Consider nodes without a style context to be NOT preformatted:
|
|
|
|
// For instance, this is true of JS tags inside the body (which show
|
|
|
|
// up as #text nodes but have no style context).
|
|
|
|
*aResult = PR_FALSE;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
1999-05-17 16:22:31 +04:00
|
|
|
|
|
|
|
styleText = (const nsStyleText*)styleContext->GetStyleData(eStyleStruct_Text);
|
|
|
|
|
|
|
|
bPreformatted = (NS_STYLE_WHITESPACE_PRE == styleText->mWhiteSpace) ||
|
|
|
|
(NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == styleText->mWhiteSpace);
|
|
|
|
|
|
|
|
*aResult = bPreformatted;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// IsNextCharWhitespace: checks the adjacent content in the same block
|
1999-11-25 03:16:56 +03:00
|
|
|
// to see if following selection is whitespace or nbsp
|
1999-05-17 16:22:31 +04:00
|
|
|
nsresult
|
|
|
|
nsEditor::IsNextCharWhitespace(nsIDOMNode *aParentNode,
|
|
|
|
PRInt32 aOffset,
|
1999-11-25 03:16:56 +03:00
|
|
|
PRBool *outIsSpace,
|
|
|
|
PRBool *outIsNBSP,
|
|
|
|
nsCOMPtr<nsIDOMNode> *outNode,
|
|
|
|
PRInt32 *outOffset)
|
|
|
|
{
|
|
|
|
if (!outIsSpace || !outIsNBSP) return NS_ERROR_NULL_POINTER;
|
|
|
|
*outIsSpace = PR_FALSE;
|
|
|
|
*outIsNBSP = PR_FALSE;
|
|
|
|
if (outNode) *outNode = nsnull;
|
|
|
|
if (outOffset) *outOffset = -1;
|
1999-05-17 16:22:31 +04:00
|
|
|
|
1999-11-25 03:16:56 +03:00
|
|
|
nsAutoString tempString;
|
1999-05-17 16:22:31 +04:00
|
|
|
PRUint32 strLength;
|
|
|
|
nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(aParentNode);
|
|
|
|
if (textNode)
|
|
|
|
{
|
|
|
|
textNode->GetLength(&strLength);
|
1999-06-08 10:04:51 +04:00
|
|
|
if ((PRUint32)aOffset < strLength)
|
1999-05-17 16:22:31 +04:00
|
|
|
{
|
|
|
|
// easy case: next char is in same node
|
|
|
|
textNode->SubstringData(aOffset,aOffset+1,tempString);
|
2000-03-12 12:14:14 +03:00
|
|
|
*outIsSpace = nsCRT::IsAsciiSpace(tempString.First());
|
1999-11-25 03:16:56 +03:00
|
|
|
*outIsNBSP = (tempString.First() == nbsp);
|
|
|
|
if (outNode) *outNode = do_QueryInterface(aParentNode);
|
|
|
|
if (outOffset) *outOffset = aOffset+1; // yes, this is _past_ the character;
|
1999-05-17 16:22:31 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// harder case: next char in next node.
|
|
|
|
nsCOMPtr<nsIDOMNode> node = NextNodeInBlock(aParentNode, kIterForward);
|
|
|
|
nsCOMPtr<nsIDOMNode> tmp;
|
|
|
|
while (node)
|
|
|
|
{
|
|
|
|
if (!IsInlineNode(node)) // skip over bold, italic, link, ect nodes
|
|
|
|
{
|
1999-08-24 12:55:28 +04:00
|
|
|
if (IsTextNode(node) && IsEditable(node))
|
1999-05-17 16:22:31 +04:00
|
|
|
{
|
|
|
|
textNode = do_QueryInterface(node);
|
|
|
|
textNode->GetLength(&strLength);
|
|
|
|
if (strLength)
|
|
|
|
{
|
1999-09-22 05:23:58 +04:00
|
|
|
// you could use nsITextContent::IsOnlyWhitespace here
|
1999-05-17 16:22:31 +04:00
|
|
|
textNode->SubstringData(0,1,tempString);
|
2000-03-12 12:14:14 +03:00
|
|
|
*outIsSpace = nsCRT::IsAsciiSpace(tempString.First());
|
1999-11-25 03:16:56 +03:00
|
|
|
*outIsNBSP = (tempString.First() == nbsp);
|
|
|
|
if (outNode) *outNode = do_QueryInterface(node);
|
|
|
|
if (outOffset) *outOffset = 1; // yes, this is _past_ the character;
|
1999-05-17 16:22:31 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
1999-08-24 12:55:28 +04:00
|
|
|
// else it's an empty text node, or not editable; skip it.
|
1999-05-17 16:22:31 +04:00
|
|
|
}
|
|
|
|
else // node is an image or some other thingy that doesn't count as whitespace
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tmp = node;
|
|
|
|
node = NextNodeInBlock(tmp, kIterForward);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// IsPrevCharWhitespace: checks the adjacent content in the same block
|
|
|
|
// to see if following selection is whitespace
|
|
|
|
nsresult
|
|
|
|
nsEditor::IsPrevCharWhitespace(nsIDOMNode *aParentNode,
|
|
|
|
PRInt32 aOffset,
|
1999-11-25 03:16:56 +03:00
|
|
|
PRBool *outIsSpace,
|
|
|
|
PRBool *outIsNBSP,
|
|
|
|
nsCOMPtr<nsIDOMNode> *outNode,
|
|
|
|
PRInt32 *outOffset)
|
|
|
|
{
|
|
|
|
if (!outIsSpace || !outIsNBSP) return NS_ERROR_NULL_POINTER;
|
|
|
|
*outIsSpace = PR_FALSE;
|
|
|
|
*outIsNBSP = PR_FALSE;
|
|
|
|
if (outNode) *outNode = nsnull;
|
|
|
|
if (outOffset) *outOffset = -1;
|
1999-05-17 16:22:31 +04:00
|
|
|
|
1999-09-15 03:44:05 +04:00
|
|
|
nsAutoString tempString;
|
1999-05-17 16:22:31 +04:00
|
|
|
PRUint32 strLength;
|
|
|
|
nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(aParentNode);
|
|
|
|
if (textNode)
|
|
|
|
{
|
|
|
|
if (aOffset > 0)
|
|
|
|
{
|
|
|
|
// easy case: prev char is in same node
|
|
|
|
textNode->SubstringData(aOffset-1,aOffset,tempString);
|
2000-03-12 12:14:14 +03:00
|
|
|
*outIsSpace = nsCRT::IsAsciiSpace(tempString.First());
|
1999-11-25 03:16:56 +03:00
|
|
|
*outIsNBSP = (tempString.First() == nbsp);
|
|
|
|
if (outNode) *outNode = do_QueryInterface(aParentNode);
|
|
|
|
if (outOffset) *outOffset = aOffset-1;
|
1999-05-17 16:22:31 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// harder case: prev char in next node
|
|
|
|
nsCOMPtr<nsIDOMNode> node = NextNodeInBlock(aParentNode, kIterBackward);
|
|
|
|
nsCOMPtr<nsIDOMNode> tmp;
|
|
|
|
while (node)
|
|
|
|
{
|
|
|
|
if (!IsInlineNode(node)) // skip over bold, italic, link, ect nodes
|
|
|
|
{
|
1999-08-24 12:55:28 +04:00
|
|
|
if (IsTextNode(node) && IsEditable(node))
|
1999-05-17 16:22:31 +04:00
|
|
|
{
|
|
|
|
textNode = do_QueryInterface(node);
|
|
|
|
textNode->GetLength(&strLength);
|
|
|
|
if (strLength)
|
|
|
|
{
|
1999-09-22 05:23:58 +04:00
|
|
|
// you could use nsITextContent::IsOnlyWhitespace here
|
1999-05-17 16:22:31 +04:00
|
|
|
textNode->SubstringData(strLength-1,strLength,tempString);
|
2000-03-12 12:14:14 +03:00
|
|
|
*outIsSpace = nsCRT::IsAsciiSpace(tempString.First());
|
1999-11-25 03:16:56 +03:00
|
|
|
*outIsNBSP = (tempString.First() == nbsp);
|
|
|
|
if (outNode) *outNode = do_QueryInterface(aParentNode);
|
|
|
|
if (outOffset) *outOffset = strLength-1;
|
1999-05-17 16:22:31 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
1999-08-24 12:55:28 +04:00
|
|
|
// else it's an empty text node, or not editable; skip it.
|
1999-05-17 16:22:31 +04:00
|
|
|
}
|
|
|
|
else // node is an image or some other thingy that doesn't count as whitespace
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// otherwise we found a node we want to skip, keep going
|
|
|
|
tmp = node;
|
|
|
|
node = NextNodeInBlock(tmp, kIterBackward);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// SplitNodeDeep: this splits a node "deeply", splitting children as
|
|
|
|
// appropriate. The place to split is represented by
|
|
|
|
// a dom point at {splitPointParent, splitPointOffset}.
|
|
|
|
// That dom point must be inside aNode, which is the node to
|
1999-11-25 03:16:56 +03:00
|
|
|
// split. outOffset is set to the offset in the parent of aNode where
|
1999-08-10 01:45:52 +04:00
|
|
|
// the split terminates - where you would want to insert
|
|
|
|
// a new element, for instance, if thats why you were splitting
|
|
|
|
// the node.
|
|
|
|
//
|
1999-05-17 16:22:31 +04:00
|
|
|
nsresult
|
|
|
|
nsEditor::SplitNodeDeep(nsIDOMNode *aNode,
|
1999-05-29 01:17:30 +04:00
|
|
|
nsIDOMNode *aSplitPointParent,
|
1999-08-10 01:45:52 +04:00
|
|
|
PRInt32 aSplitPointOffset,
|
2000-04-24 15:51:12 +04:00
|
|
|
PRInt32 *outOffset,
|
|
|
|
nsCOMPtr<nsIDOMNode> *outLeftNode,
|
|
|
|
nsCOMPtr<nsIDOMNode> *outRightNode)
|
1999-05-17 16:22:31 +04:00
|
|
|
{
|
1999-08-10 01:45:52 +04:00
|
|
|
if (!aNode || !aSplitPointParent || !outOffset) return NS_ERROR_NULL_POINTER;
|
1999-05-17 16:22:31 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> nodeToSplit = do_QueryInterface(aSplitPointParent);
|
1999-08-10 01:45:52 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> tempNode, parentNode;
|
1999-05-17 16:22:31 +04:00
|
|
|
PRInt32 offset = aSplitPointOffset;
|
1999-08-10 01:45:52 +04:00
|
|
|
nsresult res;
|
1999-05-17 16:22:31 +04:00
|
|
|
|
2000-04-24 15:51:12 +04:00
|
|
|
if (outLeftNode) *outLeftNode = nsnull;
|
|
|
|
if (outRightNode) *outRightNode = nsnull;
|
|
|
|
|
1999-05-17 16:22:31 +04:00
|
|
|
while (nodeToSplit)
|
|
|
|
{
|
1999-08-31 17:55:18 +04:00
|
|
|
// need to insert rules code call here to do things like
|
1999-08-10 01:45:52 +04:00
|
|
|
// not split a list if you are after the last <li> or before the first, etc.
|
|
|
|
// for now we just have some smarts about unneccessarily splitting
|
|
|
|
// textnodes, which should be universal enough to put straight in
|
|
|
|
// this nsEditor routine.
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(nodeToSplit);
|
|
|
|
PRUint32 textLen=0;
|
|
|
|
if (nodeAsText)
|
|
|
|
nodeAsText->GetLength(&textLen);
|
|
|
|
PRBool bDoSplit = PR_FALSE;
|
|
|
|
|
1999-08-28 06:40:18 +04:00
|
|
|
if (!nodeAsText || (offset && (offset != (PRInt32)textLen)))
|
1999-08-10 01:45:52 +04:00
|
|
|
{
|
|
|
|
bDoSplit = PR_TRUE;
|
1999-08-18 12:13:06 +04:00
|
|
|
res = SplitNode(nodeToSplit, offset, getter_AddRefs(tempNode));
|
1999-08-10 01:45:52 +04:00
|
|
|
if (NS_FAILED(res)) return res;
|
2000-04-24 15:51:12 +04:00
|
|
|
if (outRightNode) *outRightNode = nodeToSplit;
|
|
|
|
if (outLeftNode) *outLeftNode = tempNode;
|
1999-08-10 01:45:52 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
res = nodeToSplit->GetParentNode(getter_AddRefs(parentNode));
|
1999-05-17 16:22:31 +04:00
|
|
|
if (NS_FAILED(res)) return res;
|
1999-08-10 01:45:52 +04:00
|
|
|
if (!parentNode) return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
if (!bDoSplit && offset) // must be "end of text node" case, we didn't split it, just move past it
|
2000-04-24 15:51:12 +04:00
|
|
|
{
|
1999-08-10 01:45:52 +04:00
|
|
|
offset = GetIndexOf(parentNode, nodeToSplit) +1;
|
2000-04-24 15:51:12 +04:00
|
|
|
if (outLeftNode) *outLeftNode = nodeToSplit;
|
|
|
|
}
|
1999-08-10 01:45:52 +04:00
|
|
|
else
|
2000-04-24 15:51:12 +04:00
|
|
|
{
|
1999-08-10 01:45:52 +04:00
|
|
|
offset = GetIndexOf(parentNode, nodeToSplit);
|
2000-04-24 15:51:12 +04:00
|
|
|
if (outRightNode) *outRightNode = nodeToSplit;
|
|
|
|
}
|
1999-05-17 16:22:31 +04:00
|
|
|
|
|
|
|
if (nodeToSplit.get() == aNode) // we split all the way up to (and including) aNode; we're done
|
|
|
|
break;
|
|
|
|
|
1999-08-10 01:45:52 +04:00
|
|
|
nodeToSplit = parentNode;
|
1999-05-17 16:22:31 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!nodeToSplit)
|
|
|
|
{
|
|
|
|
NS_NOTREACHED("null node obtained in nsEditor::SplitNodeDeep()");
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
1999-08-10 01:45:52 +04:00
|
|
|
*outOffset = offset;
|
|
|
|
|
1999-05-17 16:22:31 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// JoinNodeDeep: this joins two like nodes "deeply", joining children as
|
|
|
|
// appropriate.
|
|
|
|
nsresult
|
|
|
|
nsEditor::JoinNodeDeep(nsIDOMNode *aLeftNode,
|
1999-05-29 01:17:30 +04:00
|
|
|
nsIDOMNode *aRightNode,
|
1999-08-31 17:55:18 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> *aOutJoinNode,
|
|
|
|
PRInt32 *outOffset)
|
1999-05-17 16:22:31 +04:00
|
|
|
{
|
1999-08-31 17:55:18 +04:00
|
|
|
if (!aLeftNode || !aRightNode || !aOutJoinNode || !outOffset) return NS_ERROR_NULL_POINTER;
|
1999-05-17 16:22:31 +04:00
|
|
|
|
|
|
|
// while the rightmost children and their descendants of the left node
|
|
|
|
// match the leftmost children and their descendants of the right node
|
|
|
|
// join them up. Can you say that three times fast?
|
1999-08-31 17:55:18 +04:00
|
|
|
|
1999-05-17 16:22:31 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> leftNodeToJoin = do_QueryInterface(aLeftNode);
|
|
|
|
nsCOMPtr<nsIDOMNode> rightNodeToJoin = do_QueryInterface(aRightNode);
|
1999-08-31 17:55:18 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> parentNode,tmp;
|
1999-05-17 16:22:31 +04:00
|
|
|
nsresult res = NS_OK;
|
|
|
|
|
|
|
|
rightNodeToJoin->GetParentNode(getter_AddRefs(parentNode));
|
|
|
|
|
|
|
|
while (leftNodeToJoin && rightNodeToJoin && parentNode &&
|
|
|
|
NodesSameType(leftNodeToJoin, rightNodeToJoin))
|
|
|
|
{
|
1999-08-31 17:55:18 +04:00
|
|
|
// adjust out params
|
|
|
|
PRUint32 length;
|
|
|
|
if (IsTextNode(leftNodeToJoin))
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMCharacterData>nodeAsText;
|
|
|
|
nodeAsText = do_QueryInterface(leftNodeToJoin);
|
|
|
|
nodeAsText->GetLength(&length);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
res = GetLengthOfDOMNode(leftNodeToJoin, length);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
*aOutJoinNode = rightNodeToJoin;
|
|
|
|
*outOffset = length;
|
1999-05-17 16:22:31 +04:00
|
|
|
|
1999-08-31 17:55:18 +04:00
|
|
|
// do the join
|
|
|
|
res = JoinNodes(leftNodeToJoin, rightNodeToJoin, parentNode);
|
1999-05-17 16:22:31 +04:00
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
if (IsTextNode(parentNode)) // we've joined all the way down to text nodes, we're done!
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// get new left and right nodes, and begin anew
|
1999-08-31 17:55:18 +04:00
|
|
|
parentNode = rightNodeToJoin;
|
|
|
|
leftNodeToJoin = GetChildAt(parentNode, length-1);
|
|
|
|
rightNodeToJoin = GetChildAt(parentNode, length);
|
|
|
|
|
|
|
|
// skip over non-editable nodes
|
|
|
|
while (leftNodeToJoin && !IsEditable(leftNodeToJoin))
|
|
|
|
{
|
|
|
|
leftNodeToJoin->GetPreviousSibling(getter_AddRefs(tmp));
|
|
|
|
leftNodeToJoin = tmp;
|
|
|
|
}
|
|
|
|
if (!leftNodeToJoin) break;
|
|
|
|
|
|
|
|
while (rightNodeToJoin && !IsEditable(rightNodeToJoin))
|
|
|
|
{
|
|
|
|
rightNodeToJoin->GetNextSibling(getter_AddRefs(tmp));
|
|
|
|
rightNodeToJoin = tmp;
|
|
|
|
}
|
|
|
|
if (!rightNodeToJoin) break;
|
1999-05-17 16:22:31 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
1999-06-15 00:02:46 +04:00
|
|
|
nsresult nsEditor::BeginUpdateViewBatch()
|
|
|
|
{
|
|
|
|
NS_PRECONDITION(mUpdateCount>=0, "bad state");
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMSelection>selection;
|
2000-02-04 02:17:08 +03:00
|
|
|
nsresult rv = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_SUCCEEDED(rv) && selection)
|
|
|
|
{
|
1999-06-15 00:02:46 +04:00
|
|
|
selection->StartBatchChanges();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nsnull!=mViewManager)
|
|
|
|
{
|
|
|
|
if (0==mUpdateCount)
|
|
|
|
{
|
|
|
|
#ifdef HACK_FORCE_REDRAW
|
|
|
|
mViewManager->DisableRefresh();
|
|
|
|
#else
|
|
|
|
mViewManager->BeginUpdateViewBatch();
|
|
|
|
#endif
|
2000-02-04 02:17:08 +03:00
|
|
|
nsCOMPtr<nsIPresShell> presShell;
|
|
|
|
rv = GetPresShell(getter_AddRefs(presShell));
|
|
|
|
if (NS_SUCCEEDED(rv) && presShell)
|
|
|
|
{
|
2000-04-17 11:13:57 +04:00
|
|
|
presShell->BeginReflowBatching();
|
2000-02-04 02:17:08 +03:00
|
|
|
}
|
1999-06-15 00:02:46 +04:00
|
|
|
}
|
|
|
|
mUpdateCount++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nsresult nsEditor::EndUpdateViewBatch()
|
|
|
|
{
|
|
|
|
NS_PRECONDITION(mUpdateCount>0, "bad state");
|
1999-09-15 03:44:05 +04:00
|
|
|
|
2000-05-08 08:01:26 +04:00
|
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsISelectionController> selCon = do_QueryReferent(mSelConWeak,&rv);
|
|
|
|
if (NS_FAILED(rv) || !selCon)
|
|
|
|
return rv?rv:NS_ERROR_FAILURE;
|
1999-09-15 03:44:05 +04:00
|
|
|
|
2000-06-01 06:38:13 +04:00
|
|
|
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
|
|
|
|
nsCOMPtr<nsICaret> caret;
|
|
|
|
if (ps)
|
|
|
|
rv = ps->GetCaret(getter_AddRefs(caret));
|
|
|
|
if (NS_FAILED(rv) ||!caret)
|
|
|
|
return rv?rv:NS_ERROR_FAILURE;
|
|
|
|
StCaretHider caretHider(caret);
|
2000-01-04 06:09:41 +03:00
|
|
|
|
1999-09-15 03:44:05 +04:00
|
|
|
nsCOMPtr<nsIDOMSelection>selection;
|
|
|
|
nsresult selectionResult = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_SUCCEEDED(selectionResult) && selection) {
|
|
|
|
selection->EndBatchChanges();
|
|
|
|
}
|
1999-06-15 00:02:46 +04:00
|
|
|
|
1999-09-15 03:44:05 +04:00
|
|
|
if (mViewManager)
|
1999-06-15 00:02:46 +04:00
|
|
|
{
|
|
|
|
mUpdateCount--;
|
|
|
|
if (0==mUpdateCount)
|
|
|
|
{
|
|
|
|
#ifdef HACK_FORCE_REDRAW
|
2000-02-09 18:48:01 +03:00
|
|
|
mViewManager->EnableRefresh(NS_VMREFRESH_IMMEDIATE);
|
1999-06-15 00:02:46 +04:00
|
|
|
HACKForceRedraw();
|
|
|
|
#else
|
2000-02-09 18:48:01 +03:00
|
|
|
mViewManager->EndUpdateViewBatch(NS_VMREFRESH_IMMEDIATE);
|
1999-06-15 00:02:46 +04:00
|
|
|
#endif
|
2000-04-27 11:37:12 +04:00
|
|
|
nsCOMPtr<nsIPresShell> presShell;
|
2000-04-28 10:20:36 +04:00
|
|
|
rv = GetPresShell(getter_AddRefs(presShell));
|
2000-04-27 11:37:12 +04:00
|
|
|
if (NS_SUCCEEDED(rv) && presShell)
|
|
|
|
presShell->EndReflowBatching(PR_TRUE);
|
1999-06-15 00:02:46 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2000-01-04 06:09:41 +03:00
|
|
|
PRBool
|
|
|
|
nsEditor::GetShouldTxnSetSelection()
|
|
|
|
{
|
|
|
|
return mShouldTxnSetSelection;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
nsEditor::SetShouldTxnSetSelection(PRBool aShould)
|
|
|
|
{
|
|
|
|
mShouldTxnSetSelection = aShould;
|
|
|
|
}
|
1999-07-03 02:52:34 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
#ifdef XP_MAC
|
|
|
|
#pragma mark -
|
2000-03-24 03:26:47 +03:00
|
|
|
#pragma mark protected nsEditor methods
|
1999-08-09 05:37:50 +04:00
|
|
|
#pragma mark -
|
|
|
|
#endif
|
1999-06-08 10:04:51 +04:00
|
|
|
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP
|
1999-12-07 11:30:19 +03:00
|
|
|
nsEditor::DeleteSelectionImpl(EDirection aAction)
|
1999-06-08 10:04:51 +04:00
|
|
|
{
|
1999-12-07 11:30:19 +03:00
|
|
|
nsresult res;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-10-08 00:46:50 +04:00
|
|
|
EditAggregateTxn *txn;
|
1999-12-07 11:30:19 +03:00
|
|
|
PRInt32 i;
|
|
|
|
nsIEditActionListener *listener;
|
|
|
|
nsCOMPtr<nsIDOMSelection>selection;
|
2000-03-24 03:26:47 +03:00
|
|
|
res = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(res)) return res;
|
1999-12-07 11:30:19 +03:00
|
|
|
res = CreateTxnForDeleteSelection(aAction, &txn);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
nsAutoRules beginRulesSniffing(this, kOpDeleteSelection, aAction);
|
|
|
|
|
|
|
|
if (NS_SUCCEEDED(res))
|
|
|
|
{
|
|
|
|
if (mActionListeners)
|
|
|
|
{
|
|
|
|
for (i = 0; i < mActionListeners->Count(); i++)
|
|
|
|
{
|
|
|
|
listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
|
|
|
|
if (listener)
|
|
|
|
listener->WillDeleteSelection(selection);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
res = Do(txn);
|
|
|
|
|
|
|
|
if (mActionListeners)
|
|
|
|
{
|
|
|
|
for (i = 0; i < mActionListeners->Count(); i++)
|
|
|
|
{
|
|
|
|
listener = (nsIEditActionListener *)mActionListeners->ElementAt(i);
|
|
|
|
if (listener)
|
|
|
|
listener->DidDeleteSelection(selection);
|
|
|
|
}
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
|
1999-11-01 18:15:35 +03:00
|
|
|
// The transaction system (if any) has taken ownwership of txn
|
|
|
|
NS_IF_RELEASE(txn);
|
|
|
|
|
1999-12-07 11:30:19 +03:00
|
|
|
return res;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Non-interface, protected methods */
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::DoAfterDoTransaction(nsITransaction *aTxn)
|
|
|
|
{
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
|
|
|
|
PRBool isTransientTransaction;
|
|
|
|
rv = aTxn->GetIsTransient(&isTransientTransaction);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
if (!isTransientTransaction)
|
|
|
|
{
|
|
|
|
// we need to deal here with the case where the user saved after some
|
|
|
|
// edits, then undid one or more times. Then, the undo count is -ve,
|
|
|
|
// but we can't let a do take it back to zero. So we flip it up to
|
|
|
|
// a +ve number.
|
|
|
|
PRInt32 modCount;
|
|
|
|
GetDocModCount(modCount);
|
|
|
|
if (modCount < 0)
|
|
|
|
modCount = -modCount;
|
|
|
|
|
1999-08-25 14:51:55 +04:00
|
|
|
rv = IncDocModCount(1); // don't count transient transactions
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::DoAfterUndoTransaction()
|
|
|
|
{
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
|
1999-08-25 14:51:55 +04:00
|
|
|
rv = IncDocModCount(-1); // all undoable transactions are non-transient
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::DoAfterRedoTransaction()
|
|
|
|
{
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
|
1999-08-25 14:51:55 +04:00
|
|
|
rv = IncDocModCount(1); // all redoable transactions are non-transient
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::DoAfterDocumentSave()
|
|
|
|
{
|
|
|
|
// the mod count is reset by nsIDiskDocument.
|
|
|
|
NotifyDocumentListeners(eDocumentStateChanged);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::CreateTxnForSetAttribute(nsIDOMElement *aElement,
|
|
|
|
const nsString& aAttribute,
|
|
|
|
const nsString& aValue,
|
|
|
|
ChangeAttributeTxn ** aTxn)
|
|
|
|
{
|
|
|
|
nsresult result = NS_ERROR_NULL_POINTER;
|
|
|
|
if (nsnull != aElement)
|
|
|
|
{
|
|
|
|
result = TransactionFactory::GetNewTransaction(ChangeAttributeTxn::GetCID(), (EditTxn **)aTxn);
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
result = (*aTxn)->Init(this, aElement, aAttribute, aValue, PR_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::CreateTxnForRemoveAttribute(nsIDOMElement *aElement,
|
|
|
|
const nsString& aAttribute,
|
|
|
|
ChangeAttributeTxn ** aTxn)
|
|
|
|
{
|
|
|
|
nsresult result = NS_ERROR_NULL_POINTER;
|
|
|
|
if (nsnull != aElement)
|
|
|
|
{
|
|
|
|
result = TransactionFactory::GetNewTransaction(ChangeAttributeTxn::GetCID(), (EditTxn **)aTxn);
|
|
|
|
if (NS_SUCCEEDED(result))
|
|
|
|
{
|
|
|
|
nsAutoString value;
|
|
|
|
result = (*aTxn)->Init(this, aElement, aAttribute, value, PR_TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsEditor::CreateTxnForCreateElement(const nsString& aTag,
|
|
|
|
nsIDOMNode *aParent,
|
|
|
|
PRInt32 aPosition,
|
|
|
|
CreateElementTxn ** aTxn)
|
|
|
|
{
|
|
|
|
nsresult result = NS_ERROR_NULL_POINTER;
|
|
|
|
if (nsnull != aParent)
|
|
|
|
{
|
|
|
|
result = TransactionFactory::GetNewTransaction(CreateElementTxn::GetCID(), (EditTxn **)aTxn);
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
result = (*aTxn)->Init(this, aTag, aParent, aPosition);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsEditor::CreateTxnForInsertElement(nsIDOMNode * aNode,
|
|
|
|
nsIDOMNode * aParent,
|
|
|
|
PRInt32 aPosition,
|
|
|
|
InsertElementTxn ** aTxn)
|
|
|
|
{
|
|
|
|
nsresult result = NS_ERROR_NULL_POINTER;
|
|
|
|
if (aNode && aParent && aTxn)
|
|
|
|
{
|
|
|
|
result = TransactionFactory::GetNewTransaction(InsertElementTxn::GetCID(), (EditTxn **)aTxn);
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
result = (*aTxn)->Init(aNode, aParent, aPosition, this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsEditor::CreateTxnForDeleteElement(nsIDOMNode * aElement,
|
|
|
|
DeleteElementTxn ** aTxn)
|
|
|
|
{
|
|
|
|
nsresult result = NS_ERROR_NULL_POINTER;
|
|
|
|
if (nsnull != aElement)
|
|
|
|
{
|
|
|
|
result = TransactionFactory::GetNewTransaction(DeleteElementTxn::GetCID(), (EditTxn **)aTxn);
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
result = (*aTxn)->Init(aElement);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-12-07 11:30:19 +03:00
|
|
|
/*NS_IMETHODIMP nsEditor::CreateAggregateTxnForDeleteSelection(nsIAtom *aTxnName, EditAggregateTxn **aAggTxn)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
|
|
|
nsresult result = NS_ERROR_NULL_POINTER;
|
|
|
|
if (aAggTxn)
|
|
|
|
{
|
|
|
|
*aAggTxn = nsnull;
|
|
|
|
result = TransactionFactory::GetNewTransaction(EditAggregateTxn::GetCID(), (EditTxn**)aAggTxn);
|
|
|
|
|
|
|
|
if (NS_FAILED(result) || !*aAggTxn) {
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the name for the aggregate transaction
|
|
|
|
(*aAggTxn)->SetName(aTxnName);
|
|
|
|
|
|
|
|
// Get current selection and setup txn to delete it,
|
|
|
|
// but only if selection exists (is not a collapsed "caret" state)
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
1999-08-25 14:51:55 +04:00
|
|
|
if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
|
|
|
|
if (!ps) return NS_ERROR_NOT_INITIALIZED;
|
2000-04-27 11:37:12 +04:00
|
|
|
result = ps->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
|
1999-08-09 05:37:50 +04:00
|
|
|
if (NS_SUCCEEDED(result) && selection)
|
|
|
|
{
|
|
|
|
PRBool collapsed;
|
|
|
|
result = selection->GetIsCollapsed(&collapsed);
|
|
|
|
if (NS_SUCCEEDED(result) && !collapsed) {
|
|
|
|
EditAggregateTxn *delSelTxn;
|
1999-12-07 11:30:19 +03:00
|
|
|
result = CreateTxnForDeleteSelection(eNone, &delSelTxn);
|
1999-08-09 05:37:50 +04:00
|
|
|
if (NS_SUCCEEDED(result) && delSelTxn) {
|
|
|
|
(*aAggTxn)->AppendChild(delSelTxn);
|
2000-03-23 04:14:49 +03:00
|
|
|
NS_RELEASE(delSelTxn);
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
1999-12-07 11:30:19 +03:00
|
|
|
*/
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::CreateTxnForIMEText(const nsString & aStringToInsert,
|
|
|
|
IMETextTxn ** aTxn)
|
|
|
|
{
|
1999-10-18 18:48:41 +04:00
|
|
|
NS_ASSERTION(aTxn, "illegal value- null ptr- aTxn");
|
1999-10-26 22:54:47 +04:00
|
|
|
if(!aTxn) return NS_ERROR_NULL_POINTER;
|
2000-01-04 06:09:41 +03:00
|
|
|
|
1999-08-25 14:51:55 +04:00
|
|
|
nsresult result;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
result = TransactionFactory::GetNewTransaction(IMETextTxn::GetCID(), (EditTxn **)aTxn);
|
1999-08-25 14:51:55 +04:00
|
|
|
if (nsnull!=*aTxn) {
|
2000-04-28 10:20:36 +04:00
|
|
|
result = (*aTxn)->Init(mIMETextNode,mIMETextOffset,mIMEBufferLength,mIMETextRangeList,aStringToInsert,mSelConWeak);
|
1999-08-25 14:51:55 +04:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
result = NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
return result;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::CreateTxnForAddStyleSheet(nsICSSStyleSheet* aSheet, AddStyleSheetTxn* *aTxn)
|
|
|
|
{
|
|
|
|
nsresult rv = TransactionFactory::GetNewTransaction(AddStyleSheetTxn::GetCID(), (EditTxn **)aTxn);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
if (! *aTxn)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
|
|
|
return (*aTxn)->Init(this, aSheet);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::CreateTxnForRemoveStyleSheet(nsICSSStyleSheet* aSheet, RemoveStyleSheetTxn* *aTxn)
|
|
|
|
{
|
|
|
|
nsresult rv = TransactionFactory::GetNewTransaction(RemoveStyleSheetTxn::GetCID(), (EditTxn **)aTxn);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
if (! *aTxn)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
|
|
|
return (*aTxn)->Init(this, aSheet);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
1999-12-07 11:30:19 +03:00
|
|
|
nsEditor::CreateTxnForDeleteSelection(nsIEditor::EDirection aAction,
|
1999-12-15 02:07:12 +03:00
|
|
|
EditAggregateTxn ** aTxn)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
|
|
|
if (!aTxn)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
*aTxn = nsnull;
|
|
|
|
|
1999-12-15 02:07:12 +03:00
|
|
|
#ifdef DEBUG_akkana
|
|
|
|
NS_ASSERTION(aAction != eNextWord && aAction != ePreviousWord && aAction != eToEndOfLine, "CreateTxnForDeleteSelection: unsupported action!");
|
|
|
|
#endif
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsresult result;
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
2000-05-08 08:01:26 +04:00
|
|
|
nsCOMPtr<nsISelectionController> selCon = do_QueryReferent(mSelConWeak);
|
|
|
|
if (!selCon) return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
|
1999-08-09 05:37:50 +04:00
|
|
|
if ((NS_SUCCEEDED(result)) && selection)
|
|
|
|
{
|
|
|
|
// Check whether the selection is collapsed and we should do nothing:
|
|
|
|
PRBool isCollapsed;
|
|
|
|
result = (selection->GetIsCollapsed(&isCollapsed));
|
1999-12-07 11:30:19 +03:00
|
|
|
if (NS_SUCCEEDED(result) && isCollapsed && aAction == eNone)
|
1999-08-09 05:37:50 +04:00
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
// allocate the out-param transaction
|
|
|
|
result = TransactionFactory::GetNewTransaction(EditAggregateTxn::GetCID(), (EditTxn **)aTxn);
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIEnumerator> enumerator;
|
|
|
|
result = selection->GetEnumerator(getter_AddRefs(enumerator));
|
|
|
|
if (NS_SUCCEEDED(result) && enumerator)
|
|
|
|
{
|
|
|
|
for (enumerator->First(); NS_OK!=enumerator->IsDone(); enumerator->Next())
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsISupports> currentItem;
|
|
|
|
result = enumerator->CurrentItem(getter_AddRefs(currentItem));
|
|
|
|
if ((NS_SUCCEEDED(result)) && (currentItem))
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
|
|
|
|
range->GetIsCollapsed(&isCollapsed);
|
|
|
|
if (PR_FALSE==isCollapsed)
|
|
|
|
{
|
|
|
|
DeleteRangeTxn *txn;
|
|
|
|
result = TransactionFactory::GetNewTransaction(DeleteRangeTxn::GetCID(), (EditTxn **)&txn);
|
|
|
|
if ((NS_SUCCEEDED(result)) && (nsnull!=txn))
|
|
|
|
{
|
|
|
|
txn->Init(this, range);
|
|
|
|
(*aTxn)->AppendChild(txn);
|
2000-03-23 04:14:49 +03:00
|
|
|
NS_RELEASE(txn);
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
result = NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // we have an insertion point. delete the thing in front of it or behind it, depending on aAction
|
|
|
|
result = CreateTxnForDeleteInsertionPoint(range, aAction, *aTxn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we didn't build the transaction correctly, destroy the out-param transaction so we don't leak it.
|
|
|
|
if (NS_FAILED(result))
|
|
|
|
{
|
|
|
|
NS_IF_RELEASE(*aTxn);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//XXX: currently, this doesn't handle edge conditions because GetNext/GetPrior are not implemented
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsEditor::CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange,
|
1999-12-07 11:30:19 +03:00
|
|
|
nsIEditor::EDirection
|
1999-08-09 05:37:50 +04:00
|
|
|
aAction,
|
|
|
|
EditAggregateTxn *aTxn)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> node;
|
|
|
|
PRBool isFirst;
|
|
|
|
PRBool isLast;
|
|
|
|
PRInt32 offset;
|
|
|
|
|
|
|
|
// get the node and offset of the insertion point
|
|
|
|
nsresult result = aRange->GetStartParent(getter_AddRefs(node));
|
|
|
|
if (NS_FAILED(result))
|
|
|
|
return result;
|
|
|
|
result = aRange->GetStartOffset(&offset);
|
|
|
|
if (NS_FAILED(result))
|
|
|
|
return result;
|
|
|
|
|
|
|
|
// determine if the insertion point is at the beginning, middle, or end of the node
|
|
|
|
nsCOMPtr<nsIDOMCharacterData> nodeAsText;
|
|
|
|
nodeAsText = do_QueryInterface(node);
|
|
|
|
|
|
|
|
if (nodeAsText)
|
|
|
|
{
|
|
|
|
PRUint32 count;
|
|
|
|
nodeAsText->GetLength(&count);
|
|
|
|
isFirst = PRBool(0==offset);
|
|
|
|
isLast = PRBool(count==(PRUint32)offset);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// get the child list and count
|
|
|
|
nsCOMPtr<nsIDOMNodeList>childList;
|
|
|
|
PRUint32 count=0;
|
|
|
|
result = node->GetChildNodes(getter_AddRefs(childList));
|
|
|
|
if ((NS_SUCCEEDED(result)) && childList)
|
|
|
|
{
|
|
|
|
childList->GetLength(&count);
|
|
|
|
}
|
|
|
|
isFirst = PRBool(0==offset);
|
|
|
|
isLast = PRBool((count-1)==(PRUint32)offset);
|
|
|
|
}
|
|
|
|
// XXX: if isFirst && isLast, then we'll need to delete the node
|
|
|
|
// as well as the 1 child
|
|
|
|
|
|
|
|
// build a transaction for deleting the appropriate data
|
|
|
|
// XXX: this has to come from rule section
|
1999-12-07 11:30:19 +03:00
|
|
|
if ((ePrevious==aAction) && (PR_TRUE==isFirst))
|
1999-08-09 05:37:50 +04:00
|
|
|
{ // we're backspacing from the beginning of the node. Delete the first thing to our left
|
|
|
|
nsCOMPtr<nsIDOMNode> priorNode;
|
|
|
|
result = GetPriorNode(node, PR_TRUE, getter_AddRefs(priorNode));
|
|
|
|
if ((NS_SUCCEEDED(result)) && priorNode)
|
|
|
|
{ // there is a priorNode, so delete it's last child (if text content, delete the last char.)
|
|
|
|
// if it has no children, delete it
|
|
|
|
nsCOMPtr<nsIDOMCharacterData> priorNodeAsText;
|
|
|
|
priorNodeAsText = do_QueryInterface(priorNode);
|
|
|
|
if (priorNodeAsText)
|
|
|
|
{
|
|
|
|
PRUint32 length=0;
|
|
|
|
priorNodeAsText->GetLength(&length);
|
|
|
|
if (0<length)
|
|
|
|
{
|
|
|
|
DeleteTextTxn *txn;
|
|
|
|
result = CreateTxnForDeleteText(priorNodeAsText, length-1, 1, &txn);
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
aTxn->AppendChild(txn);
|
2000-03-23 04:14:49 +03:00
|
|
|
NS_RELEASE(txn);
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // XXX: can you have an empty text node? If so, what do you do?
|
|
|
|
printf("ERROR: found a text node with 0 characters\n");
|
|
|
|
result = NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // priorNode is not text, so tell it's parent to delete it
|
|
|
|
DeleteElementTxn *txn;
|
|
|
|
result = CreateTxnForDeleteElement(priorNode, &txn);
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
aTxn->AppendChild(txn);
|
2000-03-23 04:14:49 +03:00
|
|
|
NS_RELEASE(txn);
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1999-12-07 11:30:19 +03:00
|
|
|
else if ((nsIEditor::eNext==aAction) && (PR_TRUE==isLast))
|
1999-08-09 05:37:50 +04:00
|
|
|
{ // we're deleting from the end of the node. Delete the first thing to our right
|
|
|
|
nsCOMPtr<nsIDOMNode> nextNode;
|
|
|
|
result = GetNextNode(node, PR_TRUE, getter_AddRefs(nextNode));
|
|
|
|
if ((NS_SUCCEEDED(result)) && nextNode)
|
2000-05-10 01:06:49 +04:00
|
|
|
{ // there is a nextNode, so delete it's first child (if text content, delete the first char.)
|
1999-08-09 05:37:50 +04:00
|
|
|
// if it has no children, delete it
|
|
|
|
nsCOMPtr<nsIDOMCharacterData> nextNodeAsText;
|
|
|
|
nextNodeAsText = do_QueryInterface(nextNode);
|
|
|
|
if (nextNodeAsText)
|
|
|
|
{
|
|
|
|
PRUint32 length=0;
|
|
|
|
nextNodeAsText->GetLength(&length);
|
|
|
|
if (0<length)
|
|
|
|
{
|
|
|
|
DeleteTextTxn *txn;
|
|
|
|
result = CreateTxnForDeleteText(nextNodeAsText, 0, 1, &txn);
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
aTxn->AppendChild(txn);
|
2000-03-23 04:14:49 +03:00
|
|
|
NS_RELEASE(txn);
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // XXX: can you have an empty text node? If so, what do you do?
|
|
|
|
printf("ERROR: found a text node with 0 characters\n");
|
|
|
|
result = NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // nextNode is not text, so tell it's parent to delete it
|
|
|
|
DeleteElementTxn *txn;
|
|
|
|
result = CreateTxnForDeleteElement(nextNode, &txn);
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
aTxn->AppendChild(txn);
|
2000-03-23 04:14:49 +03:00
|
|
|
NS_RELEASE(txn);
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (nodeAsText)
|
|
|
|
{ // we have text, so delete a char at the proper offset
|
1999-12-07 11:30:19 +03:00
|
|
|
if (nsIEditor::ePrevious==aAction) {
|
1999-08-09 05:37:50 +04:00
|
|
|
offset --;
|
|
|
|
}
|
|
|
|
DeleteTextTxn *txn;
|
|
|
|
result = CreateTxnForDeleteText(nodeAsText, offset, 1, &txn);
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
aTxn->AppendChild(txn);
|
2000-03-23 04:14:49 +03:00
|
|
|
NS_RELEASE(txn);
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
1999-11-17 14:30:39 +03:00
|
|
|
{ // we're either deleting a node or some text, need to dig into the next/prev node to find out
|
1999-08-31 17:55:18 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> selectedNode;
|
1999-12-07 11:30:19 +03:00
|
|
|
if (ePrevious==aAction)
|
1999-08-31 17:55:18 +04:00
|
|
|
{
|
|
|
|
result = GetPriorNode(node, offset, PR_TRUE, getter_AddRefs(selectedNode));
|
|
|
|
}
|
1999-12-07 11:30:19 +03:00
|
|
|
else if (eNext==aAction)
|
1999-08-31 17:55:18 +04:00
|
|
|
{
|
|
|
|
result = GetNextNode(node, offset, PR_TRUE, getter_AddRefs(selectedNode));
|
|
|
|
}
|
1999-11-17 14:30:39 +03:00
|
|
|
if (NS_FAILED(result)) { return result; }
|
|
|
|
if (selectedNode)
|
1999-08-31 17:55:18 +04:00
|
|
|
{
|
1999-11-17 14:30:39 +03:00
|
|
|
nsCOMPtr<nsIDOMCharacterData> selectedNodeAsText;
|
|
|
|
selectedNodeAsText = do_QueryInterface(selectedNode);
|
|
|
|
if (selectedNodeAsText)
|
|
|
|
{ // we are deleting from a text node, so do a text deletion
|
|
|
|
PRInt32 begin = 0; // default for forward delete
|
1999-12-07 11:30:19 +03:00
|
|
|
if (ePrevious==aAction)
|
1999-11-17 14:30:39 +03:00
|
|
|
{
|
|
|
|
PRUint32 length=0;
|
|
|
|
selectedNodeAsText->GetLength(&length);
|
|
|
|
if (0<length)
|
|
|
|
begin = length-1;
|
|
|
|
}
|
|
|
|
DeleteTextTxn *delTextTxn;
|
|
|
|
result = CreateTxnForDeleteText(selectedNodeAsText, begin, 1, &delTextTxn);
|
|
|
|
if (NS_FAILED(result)) { return result; }
|
|
|
|
if (!delTextTxn) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
aTxn->AppendChild(delTextTxn);
|
2000-03-23 04:14:49 +03:00
|
|
|
NS_RELEASE(delTextTxn);
|
1999-11-17 14:30:39 +03:00
|
|
|
}
|
|
|
|
else
|
1999-08-31 17:55:18 +04:00
|
|
|
{
|
1999-11-17 14:30:39 +03:00
|
|
|
DeleteElementTxn *delElementTxn;
|
|
|
|
result = CreateTxnForDeleteElement(selectedNode, &delElementTxn);
|
|
|
|
if (NS_FAILED(result)) { return result; }
|
|
|
|
if (!delElementTxn) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
aTxn->AppendChild(delElementTxn);
|
2000-03-23 04:14:49 +03:00
|
|
|
NS_RELEASE(delElementTxn);
|
1999-08-31 17:55:18 +04:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
1999-06-08 10:04:51 +04:00
|
|
|
}
|
|
|
|
|
2000-02-10 08:14:52 +03:00
|
|
|
nsresult
|
|
|
|
nsEditor::CreateRange(nsIDOMNode *aStartParent, PRInt32 aStartOffset,
|
|
|
|
nsIDOMNode *aEndParent, PRInt32 aEndOffset,
|
|
|
|
nsIDOMRange **aRange)
|
|
|
|
{
|
|
|
|
nsresult result;
|
|
|
|
result = nsComponentManager::CreateInstance(kCDOMRangeCID, nsnull,
|
|
|
|
NS_GET_IID(nsIDOMRange),
|
|
|
|
(void **)aRange);
|
|
|
|
|
|
|
|
if (NS_FAILED(result))
|
|
|
|
return result;
|
|
|
|
|
|
|
|
if (!*aRange)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
result = (*aRange)->SetStart(aStartParent, aStartOffset);
|
|
|
|
|
|
|
|
if (NS_SUCCEEDED(result))
|
|
|
|
result = (*aRange)->SetEnd(aEndParent, aEndOffset);
|
|
|
|
|
|
|
|
if (NS_FAILED(result))
|
|
|
|
{
|
|
|
|
NS_RELEASE((*aRange));
|
|
|
|
*aRange = 0;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsEditor::GetFirstNodeInRange(nsIDOMRange *aRange, nsIDOMNode **aNode)
|
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
// Note: this might return a node that is outside of the range.
|
|
|
|
// Use caqrefully.
|
2000-02-10 08:14:52 +03:00
|
|
|
if (!aRange || !aNode) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
*aNode = nsnull;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNode> startParent;
|
|
|
|
nsresult res = aRange->GetStartParent(getter_AddRefs(startParent));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!startParent) return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
PRInt32 offset;
|
|
|
|
res = aRange->GetStartOffset(&offset);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
2000-03-21 09:05:24 +03:00
|
|
|
nsCOMPtr<nsIDOMNode> child = GetChildAt(startParent, offset);
|
2000-02-10 08:14:52 +03:00
|
|
|
if (!child) return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
*aNode = child.get();
|
|
|
|
NS_ADDREF(*aNode);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsEditor::AppendNodeToSelectionAsRange(nsIDOMNode *aNode)
|
|
|
|
{
|
|
|
|
if (!aNode) return NS_ERROR_NULL_POINTER;
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
|
|
|
nsresult res = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if(!selection) return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNode> parentNode;
|
|
|
|
res = aNode->GetParentNode(getter_AddRefs(parentNode));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!parentNode) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
PRInt32 offset;
|
|
|
|
res = GetChildOffset(aNode, parentNode, offset);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMRange> range;
|
|
|
|
res = CreateRange(parentNode, offset, parentNode, offset+1, getter_AddRefs(range));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!range) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
return selection->AddRange(range);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult nsEditor::ClearSelection()
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
|
|
|
nsresult res = nsEditor::GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!selection) return NS_ERROR_FAILURE;
|
|
|
|
return selection->ClearSelection();
|
|
|
|
}
|
|
|
|
|