1999-08-20 02:11:58 +04:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
1999-03-01 22:54:47 +03:00
|
|
|
*
|
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-03-01 22:54:47 +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-03-01 22:54:47 +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-03-01 22:54:47 +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-02-01 17:26:27 +03:00
|
|
|
* Pierre Phaneuf <pp@ludusdesign.com>
|
1999-03-01 22:54:47 +03:00
|
|
|
*/
|
1999-09-18 03:15:12 +04:00
|
|
|
#include "nsICaret.h"
|
1999-03-01 22:54:47 +03:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-03-01 22:54:47 +03:00
|
|
|
#include "nsHTMLEditor.h"
|
1999-03-29 12:02:05 +04:00
|
|
|
#include "nsHTMLEditRules.h"
|
2000-01-26 03:57:37 +03:00
|
|
|
#include "nsHTMLEditUtils.h"
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-03-01 22:54:47 +03:00
|
|
|
#include "nsEditorEventListeners.h"
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-07-05 03:01:10 +04:00
|
|
|
#include "nsIDOMText.h"
|
1999-04-08 04:46:10 +04:00
|
|
|
#include "nsIDOMNodeList.h"
|
1999-03-01 22:54:47 +03:00
|
|
|
#include "nsIDOMDocument.h"
|
2000-03-24 03:26:47 +03:00
|
|
|
#include "nsIDOMAttr.h"
|
1999-06-08 04:02:25 +04:00
|
|
|
#include "nsIDocument.h"
|
1999-03-01 22:54:47 +03:00
|
|
|
#include "nsIDOMEventReceiver.h"
|
1999-11-03 10:11:45 +03:00
|
|
|
#include "nsIDOMKeyEvent.h"
|
1999-03-01 22:54:47 +03:00
|
|
|
#include "nsIDOMKeyListener.h"
|
|
|
|
#include "nsIDOMMouseListener.h"
|
1999-03-29 12:02:05 +04:00
|
|
|
#include "nsIDOMSelection.h"
|
1999-03-30 02:01:26 +04:00
|
|
|
#include "nsIDOMHTMLAnchorElement.h"
|
|
|
|
#include "nsIDOMHTMLImageElement.h"
|
1999-12-15 02:07:12 +03:00
|
|
|
#include "nsISelectionController.h"
|
2000-03-21 09:05:24 +03:00
|
|
|
#include "nsIFrameSelection.h" // For TABLESELECTION_ defines
|
1999-12-15 02:07:12 +03:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
#include "nsICSSLoader.h"
|
|
|
|
#include "nsICSSStyleSheet.h"
|
|
|
|
#include "nsIHTMLContentContainer.h"
|
|
|
|
#include "nsIStyleSet.h"
|
|
|
|
#include "nsIDocumentObserver.h"
|
|
|
|
#include "nsIDocumentStateListener.h"
|
|
|
|
|
|
|
|
#include "nsIStyleContext.h"
|
|
|
|
|
1999-04-20 21:47:12 +04:00
|
|
|
#include "nsIEnumerator.h"
|
1999-04-21 22:53:55 +04:00
|
|
|
#include "nsIContent.h"
|
|
|
|
#include "nsIContentIterator.h"
|
1999-03-01 22:54:47 +03:00
|
|
|
#include "nsEditorCID.h"
|
1999-04-21 22:53:55 +04:00
|
|
|
#include "nsLayoutCID.h"
|
|
|
|
#include "nsIDOMRange.h"
|
1999-06-11 01:31:42 +04:00
|
|
|
#include "nsIDOMNSRange.h"
|
1999-05-01 02:40:18 +04:00
|
|
|
#include "nsISupportsArray.h"
|
|
|
|
#include "nsVoidArray.h"
|
1999-05-14 00:59:08 +04:00
|
|
|
#include "nsFileSpec.h"
|
1999-03-09 12:44:27 +03:00
|
|
|
#include "nsIComponentManager.h"
|
1999-03-01 22:54:47 +03:00
|
|
|
#include "nsIServiceManager.h"
|
1999-05-27 01:40:51 +04:00
|
|
|
#include "nsWidgetsCID.h"
|
1999-06-07 23:32:36 +04:00
|
|
|
#include "nsIDocumentEncoder.h"
|
1999-06-11 01:31:42 +04:00
|
|
|
#include "nsIDOMDocumentFragment.h"
|
1999-06-10 04:35:02 +04:00
|
|
|
#include "nsIPresShell.h"
|
1999-07-19 23:37:08 +04:00
|
|
|
#include "nsIImage.h"
|
1999-08-09 05:37:50 +04:00
|
|
|
#include "nsAOLCiter.h"
|
|
|
|
#include "nsInternetCiter.h"
|
1999-08-25 12:35:06 +04:00
|
|
|
#include "nsISupportsPrimitives.h"
|
1999-09-30 00:08:15 +04:00
|
|
|
#include "InsertTextTxn.h"
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
// netwerk
|
|
|
|
#include "nsIURI.h"
|
1999-11-30 07:50:42 +03:00
|
|
|
#include "nsNetUtil.h"
|
1999-07-19 23:37:08 +04:00
|
|
|
|
|
|
|
// Drag & Drop, Clipboard
|
|
|
|
#include "nsWidgetsCID.h"
|
|
|
|
#include "nsIClipboard.h"
|
|
|
|
#include "nsITransferable.h"
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
// Transactionas
|
|
|
|
#include "PlaceholderTxn.h"
|
|
|
|
#include "nsStyleSheetTxns.h"
|
|
|
|
|
|
|
|
// Misc
|
|
|
|
#include "TextEditorTest.h"
|
|
|
|
#include "nsEditorUtils.h"
|
1999-08-28 06:40:18 +04:00
|
|
|
#include "nsIPref.h"
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-11-16 17:40:11 +03:00
|
|
|
const PRUnichar nbsp = 160;
|
1999-07-14 19:24:33 +04:00
|
|
|
|
1999-07-20 02:49:21 +04:00
|
|
|
// HACK - CID for NavDTD until we can get at dtd via the document
|
|
|
|
// {a6cf9107-15b3-11d2-932e-00805f8add32}
|
|
|
|
#define NS_CNAVDTD_CID \
|
|
|
|
{ 0xa6cf9107, 0x15b3, 0x11d2, { 0x93, 0x2e, 0x0, 0x80, 0x5f, 0x8a, 0xdd, 0x32 } }
|
1999-09-30 00:08:15 +04:00
|
|
|
static NS_DEFINE_CID(kCNavDTDCID, NS_CNAVDTD_CID);
|
1999-07-20 02:49:21 +04:00
|
|
|
|
1999-03-01 22:54:47 +03:00
|
|
|
static NS_DEFINE_CID(kHTMLEditorCID, NS_HTMLEDITOR_CID);
|
1999-04-21 22:53:55 +04:00
|
|
|
static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID);
|
2000-02-08 15:53:34 +03:00
|
|
|
static NS_DEFINE_IID(kSubtreeIteratorCID, NS_SUBTREEITERATOR_CID);
|
1999-06-08 10:04:51 +04:00
|
|
|
static NS_DEFINE_CID(kCRangeCID, NS_RANGE_CID);
|
|
|
|
static NS_DEFINE_IID(kFileWidgetCID, NS_FILEWIDGET_CID);
|
1999-08-09 05:37:50 +04:00
|
|
|
static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID);
|
1999-03-01 22:54:47 +03:00
|
|
|
|
1999-07-19 23:37:08 +04:00
|
|
|
// Drag & Drop, Clipboard Support
|
|
|
|
static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
|
|
|
|
static NS_DEFINE_CID(kCTransferableCID, NS_TRANSFERABLE_CID);
|
|
|
|
|
1999-09-02 08:48:17 +04:00
|
|
|
#if defined(NS_DEBUG) && defined(DEBUG_buster)
|
1999-09-20 08:17:05 +04:00
|
|
|
static PRBool gNoisy = PR_FALSE;
|
1999-04-20 21:47:12 +04:00
|
|
|
#else
|
|
|
|
static const PRBool gNoisy = PR_FALSE;
|
|
|
|
#endif
|
1999-03-01 22:54:47 +03:00
|
|
|
|
1999-08-27 08:12:47 +04:00
|
|
|
// Some utilities to handle annoying overloading of "A" tag for link and named anchor
|
1999-07-28 03:59:22 +04:00
|
|
|
static char hrefText[] = "href";
|
1999-08-21 02:39:48 +04:00
|
|
|
static char anchorTxt[] = "anchor";
|
1999-07-28 03:59:22 +04:00
|
|
|
static char namedanchorText[] = "namedanchor";
|
1999-09-30 00:08:15 +04:00
|
|
|
nsIAtom *nsHTMLEditor::gTypingTxnName;
|
1999-10-26 22:54:47 +04:00
|
|
|
nsIAtom *nsHTMLEditor::gIMETxnName;
|
1999-10-06 23:34:09 +04:00
|
|
|
nsIAtom *nsHTMLEditor::gDeleteTxnName;
|
1999-09-30 00:08:15 +04:00
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
// some prototypes for rules creation shortcuts
|
|
|
|
nsresult NS_NewTextEditRules(nsIEditRules** aInstancePtrResult);
|
|
|
|
nsresult NS_NewHTMLEditRules(nsIEditRules** aInstancePtrResult);
|
1999-06-10 04:35:02 +04:00
|
|
|
|
1999-08-27 08:12:47 +04:00
|
|
|
#define IsLink(s) (s.EqualsIgnoreCase(hrefText))
|
1999-08-21 02:39:48 +04:00
|
|
|
#define IsNamedAnchor(s) (s.EqualsIgnoreCase(anchorTxt) || s.EqualsIgnoreCase(namedanchorText))
|
1999-07-28 03:59:22 +04:00
|
|
|
|
|
|
|
static PRBool IsLinkNode(nsIDOMNode *aNode)
|
|
|
|
{
|
|
|
|
if (aNode)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMHTMLAnchorElement> anchor = do_QueryInterface(aNode);
|
|
|
|
if (anchor)
|
|
|
|
{
|
1999-09-15 03:40:16 +04:00
|
|
|
nsAutoString tmpText;
|
1999-07-28 03:59:22 +04:00
|
|
|
if (NS_SUCCEEDED(anchor->GetHref(tmpText)) && tmpText.GetUnicode() && tmpText.Length() != 0)
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PRBool IsNamedAnchorNode(nsIDOMNode *aNode)
|
|
|
|
{
|
|
|
|
if (aNode)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMHTMLAnchorElement> anchor = do_QueryInterface(aNode);
|
|
|
|
if (anchor)
|
|
|
|
{
|
1999-09-15 03:40:16 +04:00
|
|
|
nsAutoString tmpText;
|
1999-07-28 03:59:22 +04:00
|
|
|
if (NS_SUCCEEDED(anchor->GetName(tmpText)) && tmpText.GetUnicode() && tmpText.Length() != 0)
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
1999-06-10 04:35:02 +04:00
|
|
|
|
1999-09-22 09:52:44 +04:00
|
|
|
static PRBool IsListNode(nsIDOMNode *aNode)
|
|
|
|
{
|
|
|
|
if (aNode)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMElement>element;
|
|
|
|
element = do_QueryInterface(aNode);
|
|
|
|
if (element)
|
|
|
|
{
|
|
|
|
nsAutoString tagName;
|
|
|
|
if (NS_SUCCEEDED(element->GetTagName(tagName)))
|
|
|
|
{
|
|
|
|
tagName.ToLowerCase();
|
|
|
|
// With only 3 tests, it doesn't
|
|
|
|
// seem worth using nsAtoms
|
|
|
|
if (tagName.Equals("ol") ||
|
|
|
|
tagName.Equals("ul") ||
|
|
|
|
tagName.Equals("dl"))
|
|
|
|
{
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PRBool IsCellNode(nsIDOMNode *aNode)
|
|
|
|
{
|
|
|
|
if (aNode)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMElement>element;
|
|
|
|
element = do_QueryInterface(aNode);
|
|
|
|
if (element)
|
|
|
|
{
|
|
|
|
nsAutoString tagName;
|
|
|
|
if (NS_SUCCEEDED(element->GetTagName(tagName)))
|
|
|
|
{
|
2000-03-21 09:05:24 +03:00
|
|
|
// With only 2 tests, it doesn't
|
1999-09-22 09:52:44 +04:00
|
|
|
// seem worth using nsAtoms
|
2000-03-21 09:05:24 +03:00
|
|
|
if (tagName.EqualsIgnoreCase("td") ||
|
|
|
|
tagName.EqualsIgnoreCase("th"))
|
1999-09-22 09:52:44 +04:00
|
|
|
{
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-03-01 22:54:47 +03:00
|
|
|
nsHTMLEditor::nsHTMLEditor()
|
1999-08-09 05:37:50 +04:00
|
|
|
: nsEditor()
|
|
|
|
, mTypeInState(nsnull)
|
|
|
|
, mRules(nsnull)
|
|
|
|
, mIsComposing(PR_FALSE)
|
|
|
|
, mMaxTextLength(-1)
|
2000-03-21 09:05:24 +03:00
|
|
|
, mSelectedCellIndex(0)
|
1999-03-01 22:54:47 +03:00
|
|
|
{
|
1999-03-06 00:05:35 +03:00
|
|
|
// Done in nsEditor
|
|
|
|
// NS_INIT_REFCNT();
|
2000-01-31 13:30:12 +03:00
|
|
|
mBoldAtom = getter_AddRefs(NS_NewAtom("b"));
|
|
|
|
mItalicAtom = getter_AddRefs(NS_NewAtom("i"));
|
|
|
|
mUnderlineAtom = getter_AddRefs(NS_NewAtom("u"));
|
|
|
|
mFontAtom = getter_AddRefs(NS_NewAtom("font"));
|
2000-02-17 22:40:18 +03:00
|
|
|
mLinkAtom = getter_AddRefs(NS_NewAtom("a"));
|
2000-01-31 13:30:12 +03:00
|
|
|
|
1999-09-30 00:08:15 +04:00
|
|
|
if (!gTypingTxnName)
|
|
|
|
gTypingTxnName = NS_NewAtom("Typing");
|
1999-10-07 00:27:41 +04:00
|
|
|
else
|
|
|
|
NS_ADDREF(gTypingTxnName);
|
1999-10-26 22:54:47 +04:00
|
|
|
if (!gIMETxnName)
|
|
|
|
gIMETxnName = NS_NewAtom("IME");
|
|
|
|
else
|
|
|
|
NS_ADDREF(gIMETxnName);
|
1999-10-06 23:34:09 +04:00
|
|
|
if (!gDeleteTxnName)
|
|
|
|
gDeleteTxnName = NS_NewAtom("Deleting");
|
1999-10-07 00:27:41 +04:00
|
|
|
else
|
|
|
|
NS_ADDREF(gDeleteTxnName);
|
1999-06-10 04:35:02 +04:00
|
|
|
}
|
1999-03-01 22:54:47 +03:00
|
|
|
|
|
|
|
nsHTMLEditor::~nsHTMLEditor()
|
|
|
|
{
|
1999-11-02 08:16:41 +03:00
|
|
|
/* first, delete the transaction manager if there is one.
|
|
|
|
this will release any remaining transactions.
|
|
|
|
this is important because transactions can hold onto the atoms (gTypingTxnName, ...)
|
|
|
|
and to make the optimization (holding refcounted statics) work correctly,
|
|
|
|
the editor instance needs to hold the last refcount.
|
|
|
|
If you get this wrong, expect to deref a garbage gTypingTxnName pointer if you bring up a second editor.
|
|
|
|
*/
|
|
|
|
if (mTxnMgr) {
|
|
|
|
mTxnMgr = 0;
|
|
|
|
}
|
1999-10-07 00:27:41 +04:00
|
|
|
nsrefcnt refCount=0;
|
|
|
|
if (gTypingTxnName) // we addref'd in the constructor
|
|
|
|
{ // want to release it without nulling out the pointer.
|
|
|
|
refCount = gTypingTxnName->Release();
|
|
|
|
if (0==refCount) {
|
1999-11-02 08:16:41 +03:00
|
|
|
gTypingTxnName = nsnull;
|
1999-10-07 00:27:41 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-10-26 22:54:47 +04:00
|
|
|
if (gIMETxnName) // we addref'd in the constructor
|
|
|
|
{ // want to release it without nulling out the pointer.
|
|
|
|
refCount = gIMETxnName->Release();
|
|
|
|
if (0==refCount) {
|
|
|
|
gIMETxnName = nsnull;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-10-07 00:27:41 +04:00
|
|
|
if (gDeleteTxnName) // we addref'd in the constructor
|
|
|
|
{ // want to release it without nulling out the pointer.
|
|
|
|
refCount = gDeleteTxnName->Release();
|
|
|
|
if (0==refCount) {
|
|
|
|
gDeleteTxnName = nsnull;
|
|
|
|
}
|
|
|
|
}
|
2000-03-24 03:26:47 +03:00
|
|
|
|
|
|
|
// remove the rules as an action listener. Else we get a bad ownership loop later on.
|
|
|
|
// it's ok if the rules aren't a listener; we ignore the error.
|
|
|
|
nsCOMPtr<nsIEditActionListener> mListener = do_QueryInterface(mRules);
|
|
|
|
RemoveEditActionListener(mListener);
|
|
|
|
|
1999-03-01 22:54:47 +03:00
|
|
|
//the autopointers will clear themselves up.
|
1999-08-09 05:37:50 +04:00
|
|
|
//but we need to also remove the listeners or we have a leak
|
|
|
|
nsCOMPtr<nsIDOMSelection>selection;
|
1999-08-19 17:30:48 +04:00
|
|
|
nsresult result = GetSelection(getter_AddRefs(selection));
|
1999-09-30 00:08:15 +04:00
|
|
|
// if we don't get the selection, just skip this
|
1999-08-09 05:37:50 +04:00
|
|
|
if (NS_SUCCEEDED(result) && selection)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMSelectionListener>listener;
|
|
|
|
listener = do_QueryInterface(mTypeInState);
|
|
|
|
if (listener) {
|
|
|
|
selection->RemoveSelectionListener(listener);
|
|
|
|
}
|
|
|
|
}
|
1999-09-18 00:18:50 +04:00
|
|
|
// Don't use getDocument here, because we have no way of knowing if
|
|
|
|
// Init() was ever called. So we need to get the document ourselves,
|
|
|
|
// if it exists.
|
|
|
|
if (mDocWeak)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
1999-09-18 00:18:50 +04:00
|
|
|
nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(mDocWeak);
|
|
|
|
if (doc)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
1999-09-18 00:18:50 +04:00
|
|
|
nsCOMPtr<nsIDOMEventReceiver> erP = do_QueryInterface(doc, &result);
|
|
|
|
if (NS_SUCCEEDED(result) && erP)
|
|
|
|
{
|
|
|
|
if (mKeyListenerP) {
|
2000-02-01 17:26:27 +03:00
|
|
|
erP->RemoveEventListenerByIID(mKeyListenerP, NS_GET_IID(nsIDOMKeyListener));
|
1999-09-18 00:18:50 +04:00
|
|
|
}
|
|
|
|
if (mMouseListenerP) {
|
2000-02-01 17:26:27 +03:00
|
|
|
erP->RemoveEventListenerByIID(mMouseListenerP, NS_GET_IID(nsIDOMMouseListener));
|
1999-09-18 00:18:50 +04:00
|
|
|
}
|
1999-09-30 00:08:15 +04:00
|
|
|
if (mTextListenerP) {
|
2000-02-01 17:26:27 +03:00
|
|
|
erP->RemoveEventListenerByIID(mTextListenerP, NS_GET_IID(nsIDOMTextListener));
|
1999-09-30 00:08:15 +04:00
|
|
|
}
|
|
|
|
if (mCompositionListenerP) {
|
2000-02-01 17:26:27 +03:00
|
|
|
erP->RemoveEventListenerByIID(mCompositionListenerP, NS_GET_IID(nsIDOMCompositionListener));
|
1999-09-30 00:08:15 +04:00
|
|
|
}
|
1999-09-18 00:18:50 +04:00
|
|
|
if (mFocusListenerP) {
|
2000-02-01 17:26:27 +03:00
|
|
|
erP->RemoveEventListenerByIID(mFocusListenerP, NS_GET_IID(nsIDOMFocusListener));
|
1999-09-18 00:18:50 +04:00
|
|
|
}
|
1999-09-30 00:08:15 +04:00
|
|
|
if (mDragListenerP) {
|
2000-02-01 17:26:27 +03:00
|
|
|
erP->RemoveEventListenerByIID(mDragListenerP, NS_GET_IID(nsIDOMDragListener));
|
1999-09-18 00:18:50 +04:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
1999-09-18 00:18:50 +04:00
|
|
|
else
|
|
|
|
NS_NOTREACHED("~nsTextEditor");
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
}
|
1999-03-06 00:05:35 +03:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IF_RELEASE(mTypeInState);
|
1999-03-06 00:05:35 +03:00
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMPL_ADDREF_INHERITED(nsHTMLEditor, nsEditor)
|
|
|
|
NS_IMPL_RELEASE_INHERITED(nsHTMLEditor, nsEditor)
|
|
|
|
|
1999-03-06 00:05:35 +03:00
|
|
|
|
|
|
|
NS_IMETHODIMP nsHTMLEditor::QueryInterface(REFNSIID aIID, void** aInstancePtr)
|
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
if (!aInstancePtr)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
1999-03-06 00:05:35 +03:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
*aInstancePtr = nsnull;
|
|
|
|
|
2000-02-01 17:26:27 +03:00
|
|
|
if (aIID.Equals(NS_GET_IID(nsIHTMLEditor))) {
|
1999-03-06 00:05:35 +03:00
|
|
|
*aInstancePtr = NS_STATIC_CAST(nsIHTMLEditor*, this);
|
|
|
|
NS_ADDREF_THIS();
|
|
|
|
return NS_OK;
|
1999-03-01 22:54:47 +03:00
|
|
|
}
|
2000-02-01 17:26:27 +03:00
|
|
|
if (aIID.Equals(NS_GET_IID(nsIEditorMailSupport))) {
|
1999-08-09 05:37:50 +04:00
|
|
|
*aInstancePtr = NS_STATIC_CAST(nsIEditorMailSupport*, this);
|
|
|
|
NS_ADDREF_THIS();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2000-02-01 17:26:27 +03:00
|
|
|
if (aIID.Equals(NS_GET_IID(nsITableEditor))) {
|
1999-08-09 05:37:50 +04:00
|
|
|
*aInstancePtr = NS_STATIC_CAST(nsITableEditor*, this);
|
|
|
|
NS_ADDREF_THIS();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2000-02-01 17:26:27 +03:00
|
|
|
if (aIID.Equals(NS_GET_IID(nsIEditorStyleSheets))) {
|
1999-08-09 05:37:50 +04:00
|
|
|
*aInstancePtr = NS_STATIC_CAST(nsIEditorStyleSheets*, this);
|
|
|
|
NS_ADDREF_THIS();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2000-02-01 17:26:27 +03:00
|
|
|
if (aIID.Equals(NS_GET_IID(nsICSSLoaderObserver))) {
|
1999-12-04 04:29:18 +03:00
|
|
|
*aInstancePtr = NS_STATIC_CAST(nsICSSLoaderObserver*, this);
|
|
|
|
NS_ADDREF_THIS();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
return nsEditor::QueryInterface(aIID, aInstancePtr);
|
1999-03-01 22:54:47 +03:00
|
|
|
}
|
|
|
|
|
1999-03-06 00:05:35 +03:00
|
|
|
|
Preparation for ender-based text control
* added focus listener. Doesn't do much yet, but when focus notifications start appearing, we'll be ready for them. The code is in
place to hide selection when we lose focus and paint selection when we get focus. That's probably not quite right, but it's a start.
We will need to be able to determine the distinction between losing focus to another control within our app, and losing focus to
another app.
* added support for disabled and readonly states in the editor. This is accomplished by having flags set by the client, and letting the
rules system deal with those flags. The flags I added are:
TEXT_EDITOR_FLAG_PLAINTEXT 0x01 // only plain text editing is allowed
TEXT_EDITOR_FLAG_SINGLELINE 0x02 // enter key and CR-LF handled specially
TEXT_EDITOR_FLAG_PASSWORD 0x04 // text is not entered into content, only a representative character
TEXT_EDITOR_FLAG_READONLY 0x08 // editing events are disabled. Editor may still accept focus.
TEXT_EDITOR_FLAG_DISALBED 0x10 // all events are disabled (like scrolling). Editor will not accept focus.
* added WillInsertBreak/DidInsertBreak into text rules, so flags could be checked. This gets us readonly, disabled, and single line
behavior.
* cleaned up the code that allocates, registers, and destroys event listeners. Thanks to Kin and Simon for cleaning up the
ownership model on the listeners, it was a big help.
* added support for a max text length. You can now tell the text editor, be no bigger than n characters.
1999-05-29 01:24:18 +04:00
|
|
|
NS_IMETHODIMP nsHTMLEditor::Init(nsIDOMDocument *aDoc,
|
1999-08-09 05:37:50 +04:00
|
|
|
nsIPresShell *aPresShell, PRUint32 aFlags)
|
1999-03-01 22:54:47 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_PRECONDITION(aDoc && aPresShell, "bad arg");
|
|
|
|
if (!aDoc || !aPresShell)
|
1999-09-30 00:08:15 +04:00
|
|
|
return NS_ERROR_NULL_POINTER;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
nsresult result = NS_ERROR_NULL_POINTER;
|
|
|
|
// Init the base editor
|
|
|
|
result = nsEditor::Init(aDoc, aPresShell, aFlags);
|
1999-08-19 17:30:48 +04:00
|
|
|
if (NS_FAILED(result)) { return result; }
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-11-11 22:22:30 +03:00
|
|
|
nsCOMPtr<nsIDOMElement> bodyElement;
|
|
|
|
result = nsEditor::GetBodyElement(getter_AddRefs(bodyElement));
|
|
|
|
if (NS_FAILED(result)) { return result; }
|
|
|
|
if (!bodyElement) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
// init the type-in state
|
|
|
|
mTypeInState = new TypeInState();
|
|
|
|
if (!mTypeInState) {return NS_ERROR_NULL_POINTER;}
|
|
|
|
NS_ADDREF(mTypeInState);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMSelection>selection;
|
1999-08-19 17:30:48 +04:00
|
|
|
result = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(result)) { return result; }
|
1999-08-09 05:37:50 +04:00
|
|
|
if (selection)
|
1999-03-01 22:54:47 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMSelectionListener>listener;
|
|
|
|
listener = do_QueryInterface(mTypeInState);
|
|
|
|
if (listener) {
|
|
|
|
selection->AddSelectionListener(listener);
|
1999-07-20 02:49:21 +04:00
|
|
|
}
|
1999-03-01 22:54:47 +03:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-08-23 10:42:04 +04:00
|
|
|
// Set up a DTD XXX XXX
|
|
|
|
// HACK: This should have happened in a document specific way
|
|
|
|
// in nsEditor::Init(), but we dont' have a way to do that yet
|
|
|
|
result = nsComponentManager::CreateInstance(kCNavDTDCID, nsnull,
|
2000-02-01 17:26:27 +03:00
|
|
|
NS_GET_IID(nsIDTD), getter_AddRefs(mDTD));
|
1999-08-23 10:42:04 +04:00
|
|
|
if (!mDTD) result = NS_ERROR_FAILURE;
|
2000-03-24 03:26:47 +03:00
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
|
1999-11-29 11:28:46 +03:00
|
|
|
// Init the rules system
|
2000-03-24 03:26:47 +03:00
|
|
|
result = InitRules();
|
|
|
|
if (NS_FAILED(result)) return result;
|
1999-11-29 11:28:46 +03:00
|
|
|
|
|
|
|
EnableUndo(PR_TRUE);
|
|
|
|
|
1999-08-23 10:42:04 +04:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-10-05 03:50:27 +04:00
|
|
|
NS_IMETHODIMP
|
2000-01-27 06:35:44 +03:00
|
|
|
nsHTMLEditor::SetDocumentCharacterSet(const PRUnichar* characterSet)
|
|
|
|
{
|
|
|
|
nsresult result;
|
|
|
|
|
|
|
|
result = nsEditor::SetDocumentCharacterSet(characterSet);
|
|
|
|
|
|
|
|
// update META charset tag
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
nsCOMPtr<nsIDOMDocument>domdoc;
|
|
|
|
result = GetDocument(getter_AddRefs(domdoc));
|
|
|
|
if (NS_SUCCEEDED(result) && domdoc) {
|
|
|
|
nsAutoString newMetaString;
|
|
|
|
nsCOMPtr<nsIDOMNodeList>metaList;
|
|
|
|
nsCOMPtr<nsIDOMNode>metaNode;
|
|
|
|
nsCOMPtr<nsIDOMElement>metaElement;
|
|
|
|
PRBool newMetaCharset = PR_TRUE;
|
|
|
|
|
|
|
|
// get a list of META tags
|
|
|
|
result = domdoc->GetElementsByTagName("meta", getter_AddRefs(metaList));
|
|
|
|
if (NS_SUCCEEDED(result) && metaList) {
|
|
|
|
PRUint32 listLength = 0;
|
|
|
|
(void) metaList->GetLength(&listLength);
|
|
|
|
|
|
|
|
for (PRUint32 i = 0; i < listLength; i++) {
|
|
|
|
metaList->Item(i, getter_AddRefs(metaNode));
|
|
|
|
if (!metaNode) continue;
|
|
|
|
metaElement = do_QueryInterface(metaNode);
|
|
|
|
if (!metaElement) continue;
|
|
|
|
|
|
|
|
const nsString content("charset=");
|
|
|
|
nsString currentValue;
|
|
|
|
|
|
|
|
if (NS_FAILED(metaElement->GetAttribute("http-equiv", currentValue))) continue;
|
|
|
|
|
|
|
|
if (kNotFound != currentValue.Find("content-type", PR_TRUE)) {
|
|
|
|
if (NS_FAILED(metaElement->GetAttribute("content", currentValue))) continue;
|
|
|
|
|
|
|
|
PRInt32 offset = currentValue.Find(content.GetUnicode(), PR_TRUE);
|
2000-03-26 15:39:08 +04:00
|
|
|
if (kNotFound != offset) {
|
|
|
|
currentValue.Left(newMetaString, offset); // copy current value before "charset=" (e.g. text/html)
|
2000-01-27 06:35:44 +03:00
|
|
|
newMetaString.Append(content);
|
|
|
|
newMetaString.Append(characterSet);
|
|
|
|
result = nsEditor::SetAttribute(metaElement, "content", newMetaString);
|
|
|
|
if (NS_SUCCEEDED(result))
|
|
|
|
newMetaCharset = PR_FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newMetaCharset) {
|
|
|
|
nsCOMPtr<nsIDOMNodeList>headList;
|
2000-01-26 05:31:30 +03:00
|
|
|
nsCOMPtr<nsIDOMNode>headNode;
|
2000-01-27 06:35:44 +03:00
|
|
|
nsCOMPtr<nsIDOMNode>resultNode;
|
|
|
|
|
|
|
|
result = domdoc->GetElementsByTagName("head",getter_AddRefs(headList));
|
|
|
|
if (NS_SUCCEEDED(result) && headList) {
|
|
|
|
headList->Item(0, getter_AddRefs(headNode));
|
|
|
|
if (headNode) {
|
|
|
|
// Create a new meta charset tag
|
|
|
|
result = CreateNode("meta", headNode, 0, getter_AddRefs(resultNode));
|
|
|
|
if (NS_FAILED(result))
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
// Set attributes to the created element
|
|
|
|
if (resultNode && nsCRT::strlen(characterSet) > 0) {
|
|
|
|
metaElement = do_QueryInterface(resultNode);
|
|
|
|
if (metaElement) {
|
|
|
|
// not undoable, undo should undo CreateNode
|
|
|
|
result = metaElement->SetAttribute("http-equiv", "Content-Type");
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
2000-03-12 23:39:41 +03:00
|
|
|
newMetaString.Assign("text/html;charset=");
|
2000-01-27 06:35:44 +03:00
|
|
|
newMetaString.Append(characterSet);
|
|
|
|
// not undoable, undo should undo CreateNode
|
|
|
|
result = metaElement->SetAttribute("content", newMetaString);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
1999-10-05 03:50:27 +04:00
|
|
|
|
1999-08-23 10:42:04 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::PostCreate()
|
|
|
|
{
|
|
|
|
nsresult result = InstallEventListeners();
|
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
|
|
|
|
result = nsEditor::PostCreate();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::InstallEventListeners()
|
|
|
|
{
|
1999-08-25 14:51:55 +04:00
|
|
|
NS_ASSERTION(mDocWeak, "no document set on this editor");
|
|
|
|
if (!mDocWeak) return NS_ERROR_NOT_INITIALIZED;
|
1999-08-23 10:42:04 +04:00
|
|
|
|
|
|
|
nsresult result;
|
1999-08-09 05:37:50 +04:00
|
|
|
// get a key listener
|
|
|
|
result = NS_NewEditorKeyListener(getter_AddRefs(mKeyListenerP), this);
|
1999-08-19 17:30:48 +04:00
|
|
|
if (NS_FAILED(result)) {
|
1999-08-09 05:37:50 +04:00
|
|
|
HandleEventListenerError();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// get a mouse listener
|
|
|
|
result = NS_NewEditorMouseListener(getter_AddRefs(mMouseListenerP), this);
|
1999-08-19 17:30:48 +04:00
|
|
|
if (NS_FAILED(result)) {
|
1999-08-09 05:37:50 +04:00
|
|
|
HandleEventListenerError();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// get a text listener
|
|
|
|
result = NS_NewEditorTextListener(getter_AddRefs(mTextListenerP),this);
|
1999-08-19 17:30:48 +04:00
|
|
|
if (NS_FAILED(result)) {
|
1999-08-09 05:37:50 +04:00
|
|
|
#ifdef DEBUG_TAGUE
|
|
|
|
printf("nsTextEditor.cpp: failed to get TextEvent Listener\n");
|
|
|
|
#endif
|
1999-09-30 00:08:15 +04:00
|
|
|
HandleEventListenerError();
|
|
|
|
return result;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// get a composition listener
|
|
|
|
result = NS_NewEditorCompositionListener(getter_AddRefs(mCompositionListenerP),this);
|
1999-08-19 17:30:48 +04:00
|
|
|
if (NS_FAILED(result)) {
|
1999-08-09 05:37:50 +04:00
|
|
|
#ifdef DEBUG_TAGUE
|
|
|
|
printf("nsTextEditor.cpp: failed to get TextEvent Listener\n");
|
|
|
|
#endif
|
1999-09-30 00:08:15 +04:00
|
|
|
HandleEventListenerError();
|
|
|
|
return result;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// get a drag listener
|
|
|
|
result = NS_NewEditorDragListener(getter_AddRefs(mDragListenerP), this);
|
1999-08-19 17:30:48 +04:00
|
|
|
if (NS_FAILED(result)) {
|
1999-08-09 05:37:50 +04:00
|
|
|
HandleEventListenerError();
|
1999-08-19 17:30:48 +04:00
|
|
|
return result;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// get a focus listener
|
|
|
|
result = NS_NewEditorFocusListener(getter_AddRefs(mFocusListenerP), this);
|
1999-08-19 17:30:48 +04:00
|
|
|
if (NS_FAILED(result)) {
|
1999-08-09 05:37:50 +04:00
|
|
|
HandleEventListenerError();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// get the DOM event receiver
|
|
|
|
nsCOMPtr<nsIDOMEventReceiver> erP;
|
1999-08-25 14:51:55 +04:00
|
|
|
nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(mDocWeak);
|
|
|
|
if (!doc) return NS_ERROR_NOT_INITIALIZED;
|
2000-02-01 17:26:27 +03:00
|
|
|
result = doc->QueryInterface(NS_GET_IID(nsIDOMEventReceiver), getter_AddRefs(erP));
|
1999-08-19 17:30:48 +04:00
|
|
|
if (NS_FAILED(result)) {
|
1999-08-09 05:37:50 +04:00
|
|
|
HandleEventListenerError();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// register the event listeners with the DOM event reveiver
|
2000-02-01 17:26:27 +03:00
|
|
|
result = erP->AddEventListenerByIID(mKeyListenerP, NS_GET_IID(nsIDOMKeyListener));
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_ASSERTION(NS_SUCCEEDED(result), "failed to register key listener");
|
1999-09-30 00:08:15 +04:00
|
|
|
if (NS_SUCCEEDED(result))
|
|
|
|
{
|
2000-02-01 17:26:27 +03:00
|
|
|
result = erP->AddEventListenerByIID(mMouseListenerP, NS_GET_IID(nsIDOMMouseListener));
|
1999-09-30 00:08:15 +04:00
|
|
|
NS_ASSERTION(NS_SUCCEEDED(result), "failed to register mouse listener");
|
|
|
|
if (NS_SUCCEEDED(result))
|
|
|
|
{
|
2000-02-01 17:26:27 +03:00
|
|
|
result = erP->AddEventListenerByIID(mFocusListenerP, NS_GET_IID(nsIDOMFocusListener));
|
1999-09-30 00:08:15 +04:00
|
|
|
NS_ASSERTION(NS_SUCCEEDED(result), "failed to register focus listener");
|
|
|
|
if (NS_SUCCEEDED(result))
|
|
|
|
{
|
2000-02-01 17:26:27 +03:00
|
|
|
result = erP->AddEventListenerByIID(mTextListenerP, NS_GET_IID(nsIDOMTextListener));
|
1999-09-30 00:08:15 +04:00
|
|
|
NS_ASSERTION(NS_SUCCEEDED(result), "failed to register text listener");
|
|
|
|
if (NS_SUCCEEDED(result))
|
|
|
|
{
|
2000-02-01 17:26:27 +03:00
|
|
|
result = erP->AddEventListenerByIID(mCompositionListenerP, NS_GET_IID(nsIDOMCompositionListener));
|
1999-09-30 00:08:15 +04:00
|
|
|
NS_ASSERTION(NS_SUCCEEDED(result), "failed to register composition listener");
|
|
|
|
if (NS_SUCCEEDED(result))
|
|
|
|
{
|
2000-02-01 17:26:27 +03:00
|
|
|
result = erP->AddEventListenerByIID(mDragListenerP, NS_GET_IID(nsIDOMDragListener));
|
1999-09-30 00:08:15 +04:00
|
|
|
NS_ASSERTION(NS_SUCCEEDED(result), "failed to register drag listener");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1999-08-19 17:30:48 +04:00
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
HandleEventListenerError();
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::GetFlags(PRUint32 *aFlags)
|
|
|
|
{
|
|
|
|
if (!mRules || !aFlags) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
return mRules->GetFlags(aFlags);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::SetFlags(PRUint32 aFlags)
|
|
|
|
{
|
|
|
|
if (!mRules) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
return mRules->SetFlags(aFlags);
|
1999-03-01 22:54:47 +03:00
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
NS_IMETHODIMP nsHTMLEditor::InitRules()
|
1999-03-29 12:02:05 +04:00
|
|
|
{
|
|
|
|
// instantiate the rules for this text editor
|
|
|
|
// XXX: we should be told which set of rules to instantiate
|
2000-03-24 03:26:47 +03:00
|
|
|
nsresult res = NS_ERROR_FAILURE;
|
1999-08-09 05:37:50 +04:00
|
|
|
if (mFlags & eEditorPlaintextMask)
|
2000-03-24 03:26:47 +03:00
|
|
|
res = NS_NewTextEditRules(getter_AddRefs(mRules));
|
1999-09-30 00:08:15 +04:00
|
|
|
else
|
2000-03-24 03:26:47 +03:00
|
|
|
res = NS_NewHTMLEditRules(getter_AddRefs(mRules));
|
1999-08-09 05:37:50 +04:00
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!mRules) return NS_ERROR_UNEXPECTED;
|
|
|
|
res = mRules->Init(this, mFlags);
|
|
|
|
|
|
|
|
return res;
|
1999-03-29 12:02:05 +04:00
|
|
|
}
|
|
|
|
|
2000-01-19 00:50:15 +03:00
|
|
|
|
|
|
|
PRBool nsHTMLEditor::IsModifiable()
|
|
|
|
{
|
|
|
|
PRUint32 flags;
|
|
|
|
if (NS_SUCCEEDED(GetFlags(&flags)))
|
|
|
|
return ((flags & eEditorReadonlyMask) == 0);
|
|
|
|
else
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
#ifdef XP_MAC
|
|
|
|
#pragma mark -
|
2000-03-24 03:26:47 +03:00
|
|
|
#pragma mark nsIHTMLEditor methods
|
1999-08-09 05:37:50 +04:00
|
|
|
#pragma mark -
|
|
|
|
#endif
|
|
|
|
|
1999-11-03 10:11:45 +03:00
|
|
|
NS_IMETHODIMP nsHTMLEditor::EditorKeyPress(nsIDOMKeyEvent* aKeyEvent)
|
1999-09-13 13:37:51 +04:00
|
|
|
{
|
|
|
|
PRUint32 keyCode, character;
|
|
|
|
PRBool isShift, ctrlKey, altKey, metaKey;
|
|
|
|
nsresult res;
|
1999-09-30 00:08:15 +04:00
|
|
|
|
1999-09-13 13:37:51 +04:00
|
|
|
if (!aKeyEvent) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
if (NS_SUCCEEDED(aKeyEvent->GetKeyCode(&keyCode)) &&
|
|
|
|
NS_SUCCEEDED(aKeyEvent->GetShiftKey(&isShift)) &&
|
|
|
|
NS_SUCCEEDED(aKeyEvent->GetCtrlKey(&ctrlKey)) &&
|
|
|
|
NS_SUCCEEDED(aKeyEvent->GetAltKey(&altKey)) &&
|
|
|
|
NS_SUCCEEDED(aKeyEvent->GetMetaKey(&metaKey)))
|
|
|
|
{
|
|
|
|
// this royally blows: because tabs come in from keyDowns instead
|
|
|
|
// of keyPress, and because GetCharCode refuses to work for keyDown
|
|
|
|
// i have to play games.
|
1999-11-03 10:11:45 +03:00
|
|
|
if (keyCode == nsIDOMKeyEvent::DOM_VK_TAB) character = '\t';
|
1999-09-13 13:37:51 +04:00
|
|
|
else aKeyEvent->GetCharCode(&character);
|
|
|
|
|
1999-11-03 10:11:45 +03:00
|
|
|
if (keyCode == nsIDOMKeyEvent::DOM_VK_TAB && !(mFlags&eEditorPlaintextBit))
|
1999-09-13 13:37:51 +04:00
|
|
|
{
|
|
|
|
PRBool bHandled = PR_FALSE;
|
|
|
|
res = TabInTable(isShift, &bHandled);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (bHandled) return res;
|
|
|
|
}
|
1999-11-04 01:05:45 +03:00
|
|
|
else if (keyCode == nsIDOMKeyEvent::DOM_VK_RETURN
|
|
|
|
|| keyCode == nsIDOMKeyEvent::DOM_VK_ENTER)
|
1999-09-13 13:37:51 +04:00
|
|
|
{
|
1999-10-06 23:34:09 +04:00
|
|
|
nsAutoString empty;
|
1999-09-13 13:37:51 +04:00
|
|
|
if (isShift && !(mFlags&eEditorPlaintextBit))
|
1999-09-30 00:08:15 +04:00
|
|
|
{
|
1999-10-06 23:34:09 +04:00
|
|
|
return TypedText(empty, eTypedBR); // only inserts a br node
|
1999-09-30 00:08:15 +04:00
|
|
|
}
|
1999-09-13 13:37:51 +04:00
|
|
|
else
|
1999-10-06 23:34:09 +04:00
|
|
|
{
|
|
|
|
return TypedText(empty, eTypedBreak); // uses rules to figure out what to insert
|
|
|
|
}
|
|
|
|
}
|
2000-01-15 17:29:29 +03:00
|
|
|
|
|
|
|
// if we got here we either fell out of the tab case or have a normal character.
|
|
|
|
// Either way, treat as normal character.
|
|
|
|
if (character && !altKey && !ctrlKey && !isShift && !metaKey)
|
landing keyEvent_19991004_BRANCH
bugs # see the log of the check in into branch
author/reviewer:
mozilla/layout/base/src/nsRangeList.cpp brade/mjudge
mozilla/layout/html/forms/src/nsGfxTextControlFrame.cpp brade/ftang
mozilla/layout/events/src/nsDOMEvent.cpp brade/joki
mozilla/layout/events/src/nsEventStateManager.cpp brade/joki
mozilla/widget/public/nsGUIEvent.h akkana/ftang
mozilla/widget/src/windows/nsWindow.cpp ftang/mjudge
mozilla/widget/src/windows/nsWindow.h ftang/mjudge
mozilla/widget/src/mac/nsTextAreaWidget.cpp brade/ftang
mozilla/widget/src/mac/nsMacEventHandler.cpp brade/simon
mozilla/widget/src/xpwidgets/nsKeyBindMgr.cpp brade/ftang
mozilla/widget/src/gtk/nsGtkEventHandler.cpp akkana/?
mozilla/widget/src/gtk/nsWidget.cpp erik/ftang
mozilla/layout/xul/base/src/nsTreeCellFrame.cpp brade/ftang
mozilla/editor/base/nsEditorEventListeners.cpp brade/akkana
mozilla/editor/base/nsHTMLEditor.cpp brade/akkana
mozilla/rdf/content/src/nsXULKeyListener.cpp ftang/saari
fix the master bug- 15693
fix at least, but not limited to, the following bugs
10158,11956,6053,9333,10901,14348,6449,11845,13016,14410,15657,15307,15842,13856
1999-10-14 22:27:01 +04:00
|
|
|
{
|
2000-01-15 17:29:29 +03:00
|
|
|
nsAutoString key(character);
|
|
|
|
return TypedText(key, eTypedText);
|
1999-09-13 13:37:51 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
/* This routine is needed to provide a bottleneck for typing for logging
|
|
|
|
purposes. Can't use EditorKeyPress() (above) for that since it takes
|
|
|
|
a nsIDOMUIEvent* parameter. So instead we pass enough info through
|
|
|
|
to TypedText() to determine what action to take, but without passing
|
|
|
|
an event.
|
|
|
|
*/
|
|
|
|
NS_IMETHODIMP nsHTMLEditor::TypedText(const nsString& aString, PRInt32 aAction)
|
|
|
|
{
|
|
|
|
nsAutoPlaceHolderBatch batch(this, gTypingTxnName);
|
|
|
|
|
|
|
|
switch (aAction)
|
|
|
|
{
|
|
|
|
case eTypedText:
|
|
|
|
{
|
|
|
|
return InsertText(aString);
|
|
|
|
}
|
|
|
|
case eTypedBR:
|
2000-01-15 17:29:29 +03:00
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> brNode;
|
|
|
|
return InsertBR(&brNode); // only inserts a br node
|
|
|
|
}
|
1999-10-06 23:34:09 +04:00
|
|
|
case eTypedBreak:
|
|
|
|
{
|
|
|
|
return InsertBreak(); // uses rules to figure out what to insert
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
1999-09-13 13:37:51 +04:00
|
|
|
NS_IMETHODIMP nsHTMLEditor::TabInTable(PRBool inIsShift, PRBool *outHandled)
|
|
|
|
{
|
|
|
|
if (!outHandled) return NS_ERROR_NULL_POINTER;
|
|
|
|
*outHandled = PR_FALSE;
|
|
|
|
|
2000-04-04 18:51:26 +04:00
|
|
|
// Find enclosing table cell from the selection (cell may be the selected element)
|
|
|
|
nsCOMPtr<nsIDOMElement> cellElement;
|
|
|
|
nsresult res = GetElementOrParentByTagName("td", nsnull, getter_AddRefs(cellElement));
|
1999-09-13 13:37:51 +04:00
|
|
|
if (NS_FAILED(res)) return res;
|
2000-04-04 18:51:26 +04:00
|
|
|
// Do nothing -- we didn't find a table cell
|
|
|
|
if (!cellElement) return NS_OK;
|
|
|
|
|
|
|
|
// find enclosing table
|
|
|
|
nsCOMPtr<nsIDOMNode> tbl = GetEnclosingTable(cellElement);
|
|
|
|
if (!tbl) return res;
|
|
|
|
|
|
|
|
// advance to next cell
|
|
|
|
// first create an iterator over the table
|
|
|
|
nsCOMPtr<nsIContentIterator> iter;
|
|
|
|
res = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull,
|
|
|
|
NS_GET_IID(nsIContentIterator),
|
|
|
|
getter_AddRefs(iter));
|
1999-09-13 13:37:51 +04:00
|
|
|
if (NS_FAILED(res)) return res;
|
2000-04-04 18:51:26 +04:00
|
|
|
if (!iter) return NS_ERROR_NULL_POINTER;
|
|
|
|
nsCOMPtr<nsIContent> cTbl = do_QueryInterface(tbl);
|
|
|
|
nsCOMPtr<nsIContent> cBlock = do_QueryInterface(cellElement);
|
|
|
|
res = iter->Init(cTbl);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
// position iter at block
|
|
|
|
res = iter->PositionAt(cBlock);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
nsCOMPtr<nsIDOMNode> node;
|
|
|
|
nsCOMPtr<nsIContent> cNode;
|
|
|
|
do
|
1999-09-13 13:37:51 +04:00
|
|
|
{
|
2000-04-04 18:51:26 +04:00
|
|
|
if (inIsShift) res = iter->Prev();
|
|
|
|
else res = iter->Next();
|
1999-09-13 13:37:51 +04:00
|
|
|
if (NS_FAILED(res)) return res;
|
2000-04-04 18:51:26 +04:00
|
|
|
res = iter->CurrentNode(getter_AddRefs(cNode));
|
1999-09-13 13:37:51 +04:00
|
|
|
if (NS_FAILED(res)) return res;
|
2000-04-04 18:51:26 +04:00
|
|
|
node = do_QueryInterface(cNode);
|
|
|
|
if (IsTableCell(node) && (GetEnclosingTable(node) == tbl))
|
1999-09-13 13:37:51 +04:00
|
|
|
{
|
2000-04-04 18:51:26 +04:00
|
|
|
res = CollapseSelectionToDeepestNonTableFirstChild(nsnull, node);
|
1999-09-13 13:37:51 +04:00
|
|
|
if (NS_FAILED(res)) return res;
|
2000-04-04 18:51:26 +04:00
|
|
|
*outHandled = PR_TRUE;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
} while (iter->IsDone() == NS_ENUMERATOR_FALSE);
|
1999-09-13 13:37:51 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
NS_IMETHODIMP nsHTMLEditor::JoeCreateBR(nsCOMPtr<nsIDOMNode> *aInOutParent, PRInt32 *aInOutOffset, nsCOMPtr<nsIDOMNode> *outBRNode, EDirection aSelect)
|
1999-11-25 03:19:45 +03:00
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
if (!aInOutParent || !*aInOutParent || !aInOutOffset || !outBRNode) return NS_ERROR_NULL_POINTER;
|
1999-11-25 03:19:45 +03:00
|
|
|
*outBRNode = nsnull;
|
|
|
|
nsresult res;
|
|
|
|
|
|
|
|
// we need to insert a br. unfortunately, we may have to split a text node to do it.
|
2000-03-24 03:26:47 +03:00
|
|
|
nsCOMPtr<nsIDOMNode> node = *aInOutParent;
|
|
|
|
PRInt32 theOffset = *aInOutOffset;
|
|
|
|
nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(node);
|
1999-11-25 03:19:45 +03:00
|
|
|
nsAutoString brType("br");
|
|
|
|
nsCOMPtr<nsIDOMNode> brNode;
|
|
|
|
if (nodeAsText)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> tmp;
|
|
|
|
PRInt32 offset;
|
|
|
|
PRUint32 len;
|
|
|
|
nodeAsText->GetLength(&len);
|
2000-03-24 03:26:47 +03:00
|
|
|
GetNodeLocation(node, &tmp, &offset);
|
1999-11-25 03:19:45 +03:00
|
|
|
if (!tmp) return NS_ERROR_FAILURE;
|
2000-03-24 03:26:47 +03:00
|
|
|
if (!theOffset)
|
1999-11-25 03:19:45 +03:00
|
|
|
{
|
|
|
|
// we are already set to go
|
|
|
|
}
|
2000-03-24 03:26:47 +03:00
|
|
|
else if (theOffset == (PRInt32)len)
|
1999-11-25 03:19:45 +03:00
|
|
|
{
|
|
|
|
// update offset to point AFTER the text node
|
|
|
|
offset++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// split the text node
|
2000-03-24 03:26:47 +03:00
|
|
|
res = SplitNode(node, theOffset, getter_AddRefs(tmp));
|
1999-11-25 03:19:45 +03:00
|
|
|
if (NS_FAILED(res)) return res;
|
2000-03-24 03:26:47 +03:00
|
|
|
res = GetNodeLocation(node, &tmp, &offset);
|
1999-11-25 03:19:45 +03:00
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
}
|
|
|
|
// create br
|
|
|
|
res = CreateNode(brType, tmp, offset, getter_AddRefs(brNode));
|
|
|
|
if (NS_FAILED(res)) return res;
|
2000-03-24 03:26:47 +03:00
|
|
|
*aInOutParent = tmp;
|
|
|
|
*aInOutOffset = offset+1;
|
1999-11-25 03:19:45 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
res = CreateNode(brType, node, theOffset, getter_AddRefs(brNode));
|
1999-11-25 03:19:45 +03:00
|
|
|
if (NS_FAILED(res)) return res;
|
2000-03-24 03:26:47 +03:00
|
|
|
(*aInOutOffset)++;
|
1999-11-25 03:19:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
*outBRNode = brNode;
|
2000-02-25 07:39:30 +03:00
|
|
|
if (*outBRNode && (aSelect != eNone))
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
|
|
|
nsCOMPtr<nsIDOMNode> parent;
|
|
|
|
PRInt32 offset;
|
|
|
|
res = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
res = GetNodeLocation(*outBRNode, &parent, &offset);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (aSelect == eNext)
|
|
|
|
{
|
|
|
|
// position selection after br
|
|
|
|
selection->SetHint(PR_TRUE);
|
|
|
|
res = selection->Collapse(parent, offset+1);
|
|
|
|
}
|
|
|
|
else if (aSelect == ePrevious)
|
|
|
|
{
|
|
|
|
// position selection before br
|
|
|
|
selection->SetHint(PR_TRUE);
|
|
|
|
res = selection->Collapse(parent, offset);
|
|
|
|
}
|
|
|
|
}
|
1999-11-25 03:19:45 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
|
|
|
|
NS_IMETHODIMP nsHTMLEditor::CreateBR(nsIDOMNode *aNode, PRInt32 aOffset, nsCOMPtr<nsIDOMNode> *outBRNode, EDirection aSelect)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> parent = aNode;
|
|
|
|
PRInt32 offset = aOffset;
|
|
|
|
return JoeCreateBR(&parent, &offset, outBRNode, aSelect);
|
|
|
|
}
|
|
|
|
|
1999-09-30 00:08:15 +04:00
|
|
|
NS_IMETHODIMP nsHTMLEditor::InsertBR(nsCOMPtr<nsIDOMNode> *outBRNode)
|
1999-09-13 13:37:51 +04:00
|
|
|
{
|
|
|
|
PRBool bCollapsed;
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
|
|
|
|
1999-09-30 00:08:15 +04:00
|
|
|
if (!outBRNode) return NS_ERROR_NULL_POINTER;
|
|
|
|
*outBRNode = nsnull;
|
1999-09-13 13:37:51 +04:00
|
|
|
nsresult res = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
res = selection->GetIsCollapsed(&bCollapsed);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!bCollapsed)
|
|
|
|
{
|
1999-12-07 11:30:19 +03:00
|
|
|
res = DeleteSelection(nsIEditor::eNone);
|
1999-09-13 13:37:51 +04:00
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
}
|
|
|
|
nsCOMPtr<nsIDOMNode> selNode;
|
|
|
|
PRInt32 selOffset;
|
|
|
|
res = GetStartNodeAndOffset(selection, &selNode, &selOffset);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
1999-11-25 03:19:45 +03:00
|
|
|
res = CreateBR(selNode, selOffset, outBRNode);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
2000-02-25 07:39:30 +03:00
|
|
|
// position selection after br
|
|
|
|
selection->SetHint(PR_TRUE);
|
|
|
|
res = selection->Collapse(selNode, selOffset+1);
|
|
|
|
|
|
|
|
return res;
|
1999-09-13 13:37:51 +04:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
NS_IMETHODIMP nsHTMLEditor::SetInlineProperty(nsIAtom *aProperty,
|
1999-04-15 10:06:33 +04:00
|
|
|
const nsString *aAttribute,
|
|
|
|
const nsString *aValue)
|
1999-03-01 22:54:47 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
if (!aProperty) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
|
1999-12-08 06:39:36 +03:00
|
|
|
ForceCompositionEnd();
|
1999-03-01 22:54:47 +03:00
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
nsresult res;
|
|
|
|
nsCOMPtr<nsIDOMSelection>selection;
|
|
|
|
res = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!selection) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
PRBool isCollapsed;
|
|
|
|
selection->GetIsCollapsed(&isCollapsed);
|
|
|
|
if (isCollapsed)
|
|
|
|
{
|
|
|
|
// manipulating text attributes on a collapsed selection only sets state for the next text insertion
|
2000-03-29 16:53:23 +04:00
|
|
|
return mTypeInState->SetProp(aProperty, *aAttribute, *aValue);
|
2000-03-24 03:26:47 +03:00
|
|
|
}
|
|
|
|
|
1999-09-30 00:08:15 +04:00
|
|
|
nsAutoEditBatch batchIt(this);
|
1999-12-10 00:12:16 +03:00
|
|
|
nsAutoRules beginRulesSniffing(this, kOpInsertElement, nsIEditor::eNext);
|
2000-03-24 03:26:47 +03:00
|
|
|
nsAutoSelectionReset selectionResetter(selection, this);
|
1999-09-30 00:08:15 +04:00
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
PRBool cancel, handled;
|
1999-08-19 17:30:48 +04:00
|
|
|
nsTextRulesInfo ruleInfo(nsTextEditRules::kSetTextProperty);
|
2000-03-24 03:26:47 +03:00
|
|
|
res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
|
|
|
|
if (NS_FAILED(res)) return res;
|
1999-10-06 23:34:09 +04:00
|
|
|
if (!cancel && !handled)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
// get selection range enumerator
|
|
|
|
nsCOMPtr<nsIEnumerator> enumerator;
|
|
|
|
res = selection->GetEnumerator(getter_AddRefs(enumerator));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!enumerator) return NS_ERROR_FAILURE;
|
1999-08-19 17:30:48 +04:00
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
// loop thru the ranges in the selection
|
|
|
|
enumerator->First();
|
|
|
|
nsCOMPtr<nsISupports> currentItem;
|
|
|
|
while ((NS_ENUMERATOR_FALSE == enumerator->IsDone()))
|
|
|
|
{
|
|
|
|
res = enumerator->CurrentItem(getter_AddRefs(currentItem));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!currentItem) return NS_ERROR_FAILURE;
|
|
|
|
|
1999-08-19 17:30:48 +04:00
|
|
|
nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
|
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
// adjust range to include any ancestors who's children are entirely selected
|
|
|
|
res = PromoteInlineRange(range);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
// check for easy case: both range endpoints in same text node
|
|
|
|
nsCOMPtr<nsIDOMNode> startNode, endNode;
|
|
|
|
res = range->GetStartParent(getter_AddRefs(startNode));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
res = range->GetEndParent(getter_AddRefs(endNode));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if ((startNode == endNode) && IsTextNode(startNode))
|
|
|
|
{
|
|
|
|
// MOOSE: workaround for selection bug:
|
|
|
|
selection->ClearSelection();
|
|
|
|
|
|
|
|
PRInt32 startOffset, endOffset;
|
|
|
|
range->GetStartOffset(&startOffset);
|
|
|
|
range->GetEndOffset(&endOffset);
|
|
|
|
nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(startNode);
|
|
|
|
res = SetInlinePropertyOnTextNode(nodeAsText, startOffset, endOffset, aProperty, aAttribute, aValue);
|
|
|
|
if (NS_FAILED(res)) return res;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
// not the easy case. range not contained in single text node.
|
|
|
|
// there are up to three phases here. There are all the nodes
|
|
|
|
// reported by the subtree iterator to be processed. And there
|
|
|
|
// are potentially a starting textnode and an ending textnode
|
|
|
|
// which are only partially contained by the range.
|
|
|
|
|
|
|
|
// lets handle the nodes reported by the iterator. These nodes
|
|
|
|
// are entirely contained in the selection range. We build up
|
|
|
|
// a list of them (since doing operations on the document during
|
|
|
|
// iteration would perturb the iterator).
|
1999-08-19 17:30:48 +04:00
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
nsCOMPtr<nsIContentIterator> iter;
|
|
|
|
res = nsComponentManager::CreateInstance(kSubtreeIteratorCID, nsnull,
|
|
|
|
NS_GET_IID(nsIContentIterator),
|
|
|
|
getter_AddRefs(iter));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!iter) return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
nsCOMPtr<nsISupportsArray> arrayOfNodes;
|
|
|
|
nsCOMPtr<nsIContent> content;
|
|
|
|
nsCOMPtr<nsIDOMNode> node;
|
|
|
|
nsCOMPtr<nsISupports> isupports;
|
|
|
|
|
|
|
|
// make a array
|
|
|
|
res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
// iterate range and build up array
|
|
|
|
iter->Init(range);
|
|
|
|
while (NS_ENUMERATOR_FALSE == iter->IsDone())
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
res = iter->CurrentNode(getter_AddRefs(content));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
node = do_QueryInterface(content);
|
|
|
|
if (!node) return NS_ERROR_FAILURE;
|
|
|
|
if (IsEditable(node))
|
|
|
|
{
|
|
|
|
isupports = do_QueryInterface(node);
|
|
|
|
arrayOfNodes->AppendElement(isupports);
|
|
|
|
}
|
|
|
|
res = iter->Next();
|
|
|
|
if (NS_FAILED(res)) return res;
|
1999-08-19 17:30:48 +04:00
|
|
|
}
|
2000-03-24 03:26:47 +03:00
|
|
|
|
|
|
|
// MOOSE: workaround for selection bug:
|
|
|
|
selection->ClearSelection();
|
|
|
|
|
|
|
|
// first check the start parent of the range to see if it needs to
|
|
|
|
// be seperately handled (it does if it's a text node, due to how the
|
|
|
|
// subtree iterator works - it will not have reported it).
|
|
|
|
if (IsTextNode(startNode) && IsEditable(startNode))
|
1999-08-19 17:30:48 +04:00
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(startNode);
|
|
|
|
PRInt32 startOffset;
|
|
|
|
PRUint32 textLen;
|
|
|
|
range->GetStartOffset(&startOffset);
|
|
|
|
nodeAsText->GetLength(&textLen);
|
|
|
|
res = SetInlinePropertyOnTextNode(nodeAsText, startOffset, textLen, aProperty, aAttribute, aValue);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
// then loop through the list, set the property on each node
|
|
|
|
PRUint32 listCount;
|
|
|
|
PRUint32 j;
|
|
|
|
arrayOfNodes->Count(&listCount);
|
|
|
|
for (j = 0; j < listCount; j++)
|
|
|
|
{
|
|
|
|
isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0));
|
|
|
|
node = do_QueryInterface(isupports);
|
|
|
|
res = SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
arrayOfNodes->RemoveElementAt(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// last check the end parent of the range to see if it needs to
|
|
|
|
// be seperately handled (it does if it's a text node, due to how the
|
|
|
|
// subtree iterator works - it will not have reported it).
|
|
|
|
if (IsTextNode(endNode) && IsEditable(endNode))
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(endNode);
|
|
|
|
PRInt32 endOffset;
|
|
|
|
range->GetEndOffset(&endOffset);
|
|
|
|
res = SetInlinePropertyOnTextNode(nodeAsText, 0, endOffset, aProperty, aAttribute, aValue);
|
|
|
|
if (NS_FAILED(res)) return res;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
}
|
2000-03-24 03:26:47 +03:00
|
|
|
enumerator->Next();
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
1999-10-06 23:34:09 +04:00
|
|
|
}
|
|
|
|
if (!cancel)
|
|
|
|
{
|
1999-08-19 17:30:48 +04:00
|
|
|
// post-process
|
2000-03-24 03:26:47 +03:00
|
|
|
res = mRules->DidDoAction(selection, &ruleInfo, res);
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
2000-03-24 03:26:47 +03:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsHTMLEditor::SetInlinePropertyOnTextNode( nsIDOMCharacterData *aTextNode,
|
|
|
|
PRInt32 aStartOffset,
|
|
|
|
PRInt32 aEndOffset,
|
|
|
|
nsIAtom *aProperty,
|
|
|
|
const nsString *aAttribute,
|
|
|
|
const nsString *aValue)
|
|
|
|
{
|
|
|
|
if (!aTextNode) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
// dont need to do anything if no characters actually selected
|
|
|
|
if (aStartOffset == aEndOffset) return NS_OK;
|
|
|
|
|
|
|
|
nsresult res = NS_OK;
|
|
|
|
nsCOMPtr<nsIDOMNode> tmp, node = do_QueryInterface(aTextNode);
|
|
|
|
|
|
|
|
// dont need to do anything if property already set on node
|
|
|
|
PRBool bHasProp;
|
|
|
|
nsCOMPtr<nsIDOMNode> styleNode;
|
|
|
|
IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, bHasProp, getter_AddRefs(styleNode));
|
|
|
|
if (bHasProp) return NS_OK;
|
|
|
|
|
|
|
|
// do we need to split the text node?
|
|
|
|
PRUint32 textLen;
|
|
|
|
aTextNode->GetLength(&textLen);
|
|
|
|
|
|
|
|
if ( (PRUint32)aEndOffset != textLen )
|
|
|
|
{
|
|
|
|
// we need to split off back of text node
|
|
|
|
res = SplitNode(node, aEndOffset, getter_AddRefs(tmp));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
node = tmp; // remember left node
|
|
|
|
}
|
|
|
|
if ( aStartOffset )
|
|
|
|
{
|
|
|
|
// we need to split off front of text node
|
|
|
|
res = SplitNode(node, aStartOffset, getter_AddRefs(tmp));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
// reparent the node inside inline node with appropriate {attribute,value}
|
|
|
|
res = SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsHTMLEditor::SetInlinePropertyOnNode( nsIDOMNode *aNode,
|
|
|
|
nsIAtom *aProperty,
|
|
|
|
const nsString *aAttribute,
|
|
|
|
const nsString *aValue)
|
|
|
|
{
|
|
|
|
if (!aNode || !aProperty) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
nsresult res = NS_OK;
|
|
|
|
nsCOMPtr<nsIDOMNode> tmp;
|
|
|
|
nsAutoString tag;
|
|
|
|
aProperty->ToString(tag);
|
|
|
|
tag.ToLowerCase();
|
|
|
|
|
|
|
|
// dont need to do anything if property already set on node
|
|
|
|
PRBool bHasProp;
|
|
|
|
nsCOMPtr<nsIDOMNode> styleNode;
|
|
|
|
IsTextPropertySetByContent(aNode, aProperty, aAttribute, aValue, bHasProp, getter_AddRefs(styleNode));
|
|
|
|
if (bHasProp) return NS_OK;
|
|
|
|
|
|
|
|
// is it already the right kind of node, but with wrong attribute?
|
|
|
|
if (NodeIsType(aNode, aProperty))
|
|
|
|
{
|
|
|
|
// just set the attribute on it.
|
|
|
|
// but first remove any contrary style in it's children.
|
|
|
|
res = RemoveStyleInside(aNode, aProperty, aAttribute, PR_TRUE);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(aNode);
|
|
|
|
return SetAttribute(elem, *aAttribute, *aValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
// can it be put inside inline node?
|
|
|
|
if (TagCanContain(tag, aNode))
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> priorNode, nextNode;
|
|
|
|
// is either of it's neighbors the right kind of node?
|
|
|
|
GetPriorHTMLSibling(aNode, &priorNode);
|
|
|
|
GetNextHTMLSibling(aNode, &nextNode);
|
|
|
|
if (priorNode && NodeIsType(priorNode, aProperty) &&
|
|
|
|
HasAttrVal(priorNode, aAttribute, aValue) &&
|
|
|
|
IsOnlyAttribute(priorNode, aAttribute) )
|
|
|
|
{
|
|
|
|
// previous sib is already right kind of inline node; slide this over into it
|
|
|
|
res = MoveNode(aNode, priorNode, -1);
|
|
|
|
}
|
|
|
|
else if (nextNode && NodeIsType(nextNode, aProperty) &&
|
|
|
|
HasAttrVal(nextNode, aAttribute, aValue) &&
|
|
|
|
IsOnlyAttribute(priorNode, aAttribute) )
|
|
|
|
{
|
|
|
|
// following sib is already right kind of inline node; slide this over into it
|
|
|
|
res = MoveNode(aNode, nextNode, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// ok, chuck it in it's very own container
|
|
|
|
res = InsertContainerAbove(aNode, &tmp, tag, aAttribute, aValue);
|
|
|
|
}
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
return RemoveStyleInside(aNode, aProperty, aAttribute);
|
|
|
|
}
|
|
|
|
// none of the above? then cycle through the children.
|
|
|
|
nsCOMPtr<nsIDOMNodeList> childNodes;
|
|
|
|
res = aNode->GetChildNodes(getter_AddRefs(childNodes));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (childNodes)
|
|
|
|
{
|
|
|
|
PRInt32 j;
|
|
|
|
PRUint32 childCount;
|
|
|
|
childNodes->GetLength(&childCount);
|
|
|
|
if (childCount)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsISupportsArray> arrayOfNodes;
|
|
|
|
nsCOMPtr<nsIDOMNode> node;
|
|
|
|
nsCOMPtr<nsISupports> isupports;
|
|
|
|
|
|
|
|
// make a array
|
|
|
|
res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
// populate the list
|
|
|
|
for (j=0 ; j < (PRInt32)childCount; j++)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> childNode;
|
|
|
|
res = childNodes->Item(j, getter_AddRefs(childNode));
|
|
|
|
if ((NS_SUCCEEDED(res)) && (childNode) && IsEditable(childNode))
|
|
|
|
{
|
|
|
|
isupports = do_QueryInterface(childNode);
|
|
|
|
arrayOfNodes->AppendElement(isupports);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// then loop through the list, set the property on each node
|
|
|
|
PRUint32 listCount;
|
|
|
|
arrayOfNodes->Count(&listCount);
|
2000-03-28 04:34:26 +04:00
|
|
|
for (j = 0; j < (PRInt32)listCount; j++)
|
2000-03-24 03:26:47 +03:00
|
|
|
{
|
|
|
|
isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0));
|
|
|
|
node = do_QueryInterface(isupports);
|
|
|
|
res = SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
arrayOfNodes->RemoveElementAt(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nsresult nsHTMLEditor::SplitStyleAboveRange(nsIDOMRange *inRange,
|
|
|
|
nsIAtom *aProperty,
|
|
|
|
const nsString *aAttribute)
|
|
|
|
{
|
|
|
|
if (!inRange || !aProperty) return NS_ERROR_NULL_POINTER;
|
|
|
|
nsresult res;
|
|
|
|
nsCOMPtr<nsIDOMNode> startNode, endNode, origStartNode;
|
|
|
|
PRInt32 startOffset, endOffset, origStartOffset;
|
|
|
|
|
|
|
|
res = inRange->GetStartParent(getter_AddRefs(startNode));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
res = inRange->GetStartOffset(&startOffset);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
res = inRange->GetEndParent(getter_AddRefs(endNode));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
res = inRange->GetEndOffset(&endOffset);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
origStartNode = startNode;
|
|
|
|
origStartOffset = startOffset;
|
|
|
|
PRBool sameNode = (startNode==endNode);
|
|
|
|
|
|
|
|
// split any matching style nodes above the start of range
|
|
|
|
res = SplitStyleAbovePoint(&startNode, &startOffset, aProperty, aAttribute);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
if (sameNode && (startNode != origStartNode))
|
|
|
|
{
|
|
|
|
// our startNode got split. This changes the offset of the end of our range.
|
|
|
|
endOffset -= origStartOffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
// second verse, same as the first...
|
|
|
|
res = SplitStyleAbovePoint(&endNode, &endOffset, aProperty, aAttribute);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
// reset the range
|
|
|
|
res = inRange->SetStart(startNode, startOffset);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
res = inRange->SetEnd(endNode, endOffset);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult nsHTMLEditor::SplitStyleAbovePoint(nsCOMPtr<nsIDOMNode> *aNode,
|
|
|
|
PRInt32 *aOffset,
|
|
|
|
nsIAtom *aProperty,
|
|
|
|
const nsString *aAttribute)
|
|
|
|
{
|
|
|
|
if (!aNode || !*aNode || !aOffset) return NS_ERROR_NULL_POINTER;
|
|
|
|
// split any matching style nodes above the node/offset
|
|
|
|
nsCOMPtr<nsIDOMNode> parent, tmp = *aNode;
|
|
|
|
PRInt32 offset;
|
|
|
|
while (tmp && !nsHTMLEditUtils::IsBody(tmp))
|
|
|
|
{
|
|
|
|
if (NodeIsType(tmp, aProperty))
|
|
|
|
{
|
|
|
|
// found a style node we need to split
|
|
|
|
SplitNodeDeep(tmp, *aNode, *aOffset, &offset);
|
|
|
|
// reset startNode/startOffset
|
|
|
|
tmp->GetParentNode(getter_AddRefs(*aNode));
|
|
|
|
*aOffset = offset;
|
|
|
|
}
|
|
|
|
tmp->GetParentNode(getter_AddRefs(parent));
|
|
|
|
tmp = parent;
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult nsHTMLEditor::RemoveStyleInside(nsIDOMNode *aNode,
|
|
|
|
nsIAtom *aProperty,
|
|
|
|
const nsString *aAttribute,
|
|
|
|
PRBool aChildrenOnly)
|
|
|
|
{
|
|
|
|
if (!aNode || !aProperty) return NS_ERROR_NULL_POINTER;
|
|
|
|
if (IsTextNode(aNode)) return NS_OK;
|
|
|
|
nsresult res = NS_OK;
|
|
|
|
|
|
|
|
// first process the children
|
|
|
|
nsCOMPtr<nsIDOMNode> child, tmp;
|
|
|
|
aNode->GetFirstChild(getter_AddRefs(child));
|
|
|
|
while (child)
|
|
|
|
{
|
|
|
|
// cache next sibling since we might remove child
|
|
|
|
child->GetNextSibling(getter_AddRefs(tmp));
|
|
|
|
res = RemoveStyleInside(child, aProperty, aAttribute);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
child = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
// then process the node itself
|
|
|
|
if (!aChildrenOnly && NodeIsType(aNode, aProperty))
|
|
|
|
{
|
|
|
|
// if we weren't passed an attribute, then we want to
|
|
|
|
// remove any matching inlinestyles entirely
|
|
|
|
if (!aAttribute || aAttribute->IsEmpty())
|
|
|
|
{
|
|
|
|
res = RemoveContainer(aNode);
|
|
|
|
}
|
|
|
|
// otherwise we just want to eliminate the attribute
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (HasAttr(aNode, aAttribute))
|
|
|
|
{
|
|
|
|
// if this matching attribute is the ONLY one on the node,
|
|
|
|
// then remove the whole node. Otherwise just nix the attribute.
|
|
|
|
if (IsOnlyAttribute(aNode, aAttribute))
|
|
|
|
{
|
|
|
|
res = RemoveContainer(aNode);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(aNode);
|
|
|
|
if (!elem) return NS_ERROR_NULL_POINTER;
|
|
|
|
res = RemoveAttribute(elem, *aAttribute);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool nsHTMLEditor::IsOnlyAttribute(nsIDOMNode *aNode,
|
|
|
|
const nsString *aAttribute)
|
|
|
|
{
|
|
|
|
if (!aNode || !aAttribute) return PR_FALSE; // ooops
|
|
|
|
nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
|
|
|
|
if (!content) return PR_FALSE; // ooops
|
|
|
|
|
|
|
|
PRInt32 attrCount, i, nameSpaceID;
|
|
|
|
nsIAtom* attrName;
|
|
|
|
content->GetAttributeCount(attrCount);
|
|
|
|
|
|
|
|
for (i=0; i<attrCount; i++)
|
|
|
|
{
|
|
|
|
content->GetAttributeNameAt(i, nameSpaceID, attrName);
|
|
|
|
nsAutoString attrString, tmp;
|
|
|
|
if (!attrName) continue; // ooops
|
|
|
|
attrName->ToString(attrString);
|
|
|
|
// if it's the attribute we know about, keep looking
|
|
|
|
if (attrString.EqualsIgnoreCase(*aAttribute)) continue;
|
|
|
|
// if it's a special _moz... attribute, keep looking
|
|
|
|
attrString.Left(tmp,4);
|
2000-03-26 15:39:08 +04:00
|
|
|
if (tmp.Equals("_moz")) continue;
|
2000-03-24 03:26:47 +03:00
|
|
|
// otherwise, it's another attribute, so return false
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
// if we made it through all of them without finding a real attribute
|
|
|
|
// other than aAttribute, then return PR_TRUE
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
nsHTMLEditor::HasMatchingAttributes(nsIDOMNode *aNode1,
|
|
|
|
nsIDOMNode *aNode2)
|
|
|
|
{
|
|
|
|
if (!aNode1 || !aNode2) return PR_FALSE; // ooops
|
|
|
|
nsCOMPtr<nsIContent> content1 = do_QueryInterface(aNode1);
|
|
|
|
if (!content1) return PR_FALSE; // ooops
|
|
|
|
nsCOMPtr<nsIContent> content2 = do_QueryInterface(aNode2);
|
|
|
|
if (!content2) return PR_FALSE; // ooops
|
|
|
|
|
|
|
|
PRInt32 attrCount, i, nameSpaceID, realCount1=0, realCount2=0;
|
|
|
|
nsIAtom* attrName;
|
|
|
|
nsresult res, res2;
|
|
|
|
content1->GetAttributeCount(attrCount);
|
|
|
|
nsAutoString attrString, tmp, attrVal1, attrVal2;
|
|
|
|
|
|
|
|
for (i=0; i<attrCount; i++)
|
|
|
|
{
|
|
|
|
content1->GetAttributeNameAt(i, nameSpaceID, attrName);
|
|
|
|
if (!attrName) continue; // ooops
|
|
|
|
attrName->ToString(attrString);
|
|
|
|
// if it's a special _moz... attribute, keep going
|
|
|
|
attrString.Left(tmp,4);
|
2000-03-26 15:39:08 +04:00
|
|
|
if (tmp.Equals("_moz")) continue;
|
2000-03-24 03:26:47 +03:00
|
|
|
// otherwise, it's another attribute, so count it
|
|
|
|
realCount1++;
|
|
|
|
// and compare it to element2's attributes
|
|
|
|
res = content1->GetAttribute(nameSpaceID, attrName, attrVal1);
|
|
|
|
res2 = content2->GetAttribute(nameSpaceID, attrName, attrVal2);
|
|
|
|
if (res != res2) return PR_FALSE;
|
|
|
|
if (!attrVal1.EqualsIgnoreCase(attrVal2)) return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
content2->GetAttributeCount(attrCount);
|
|
|
|
for (i=0; i<attrCount; i++)
|
|
|
|
{
|
|
|
|
content2->GetAttributeNameAt(i, nameSpaceID, attrName);
|
|
|
|
if (!attrName) continue; // ooops
|
|
|
|
attrName->ToString(attrString);
|
|
|
|
// if it's a special _moz... attribute, keep going
|
|
|
|
attrString.Left(tmp,4);
|
2000-03-26 15:39:08 +04:00
|
|
|
if (tmp.Equals("_moz")) continue;
|
2000-03-24 03:26:47 +03:00
|
|
|
// otherwise, it's another attribute, so count it
|
|
|
|
realCount2++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (realCount1 != realCount2) return PR_FALSE;
|
|
|
|
// otherwise, attribute counts match, and we already compared them
|
|
|
|
// when going through the first list, so we're done.
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool nsHTMLEditor::HasAttr(nsIDOMNode *aNode,
|
|
|
|
const nsString *aAttribute)
|
|
|
|
{
|
|
|
|
if (!aNode) return PR_FALSE;
|
|
|
|
if (!aAttribute || aAttribute->IsEmpty()) return PR_TRUE; // everybody has the 'null' attribute
|
|
|
|
|
|
|
|
// get element
|
|
|
|
nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(aNode);
|
|
|
|
if (!elem) return PR_FALSE;
|
|
|
|
|
|
|
|
// get attribute node
|
|
|
|
nsCOMPtr<nsIDOMAttr> attNode;
|
|
|
|
nsresult res = elem->GetAttributeNode(*aAttribute, getter_AddRefs(attNode));
|
|
|
|
if ((NS_FAILED(res)) || !attNode) return PR_FALSE;
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PRBool nsHTMLEditor::HasAttrVal(nsIDOMNode *aNode,
|
2000-03-29 16:53:23 +04:00
|
|
|
const nsString *aAttribute,
|
|
|
|
const nsString *aValue)
|
2000-03-24 03:26:47 +03:00
|
|
|
{
|
|
|
|
if (!aNode) return PR_FALSE;
|
|
|
|
if (!aAttribute || aAttribute->IsEmpty()) return PR_TRUE; // everybody has the 'null' attribute
|
|
|
|
|
|
|
|
// get element
|
|
|
|
nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(aNode);
|
|
|
|
if (!elem) return PR_FALSE;
|
|
|
|
|
|
|
|
// get attribute node
|
|
|
|
nsCOMPtr<nsIDOMAttr> attNode;
|
|
|
|
nsresult res = elem->GetAttributeNode(*aAttribute, getter_AddRefs(attNode));
|
|
|
|
if ((NS_FAILED(res)) || !attNode) return PR_FALSE;
|
|
|
|
|
|
|
|
// check if attribute has a value
|
|
|
|
PRBool isSet;
|
|
|
|
attNode->GetSpecified(&isSet);
|
|
|
|
// if no value, and that's what we wanted, then return true
|
|
|
|
if (!isSet && (!aValue || aValue->IsEmpty())) return PR_TRUE;
|
|
|
|
|
|
|
|
// get attribute value
|
|
|
|
nsAutoString attrVal;
|
|
|
|
attNode->GetValue(attrVal);
|
|
|
|
|
|
|
|
// do values match?
|
|
|
|
if (attrVal.EqualsIgnoreCase(*aValue)) return PR_TRUE;
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nsresult nsHTMLEditor::PromoteInlineRange(nsIDOMRange *inRange)
|
|
|
|
{
|
|
|
|
if (!inRange) return NS_ERROR_NULL_POINTER;
|
|
|
|
nsresult res;
|
|
|
|
nsCOMPtr<nsIDOMNode> startNode, endNode, parent;
|
|
|
|
PRInt32 startOffset, endOffset;
|
|
|
|
|
|
|
|
res = inRange->GetStartParent(getter_AddRefs(startNode));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
res = inRange->GetStartOffset(&startOffset);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
res = inRange->GetEndParent(getter_AddRefs(endNode));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
res = inRange->GetEndOffset(&endOffset);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
while ( startNode &&
|
|
|
|
!nsHTMLEditUtils::IsBody(startNode) &&
|
|
|
|
IsAtFrontOfNode(startNode, startOffset) )
|
|
|
|
{
|
|
|
|
res = GetNodeLocation(startNode, &parent, &startOffset);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
startNode = parent;
|
|
|
|
}
|
|
|
|
if (!startNode) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
while ( endNode &&
|
|
|
|
!nsHTMLEditUtils::IsBody(endNode) &&
|
|
|
|
IsAtEndOfNode(endNode, endOffset) )
|
|
|
|
{
|
|
|
|
res = GetNodeLocation(endNode, &parent, &endOffset);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
endNode = parent;
|
|
|
|
endOffset++; // we are AFTER this node
|
|
|
|
}
|
|
|
|
if (!endNode) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
res = inRange->SetStart(startNode, startOffset);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
res = inRange->SetEnd(endNode, endOffset);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool nsHTMLEditor::IsAtFrontOfNode(nsIDOMNode *aNode, PRInt32 aOffset)
|
|
|
|
{
|
|
|
|
if (!aNode) return PR_FALSE; // oops
|
|
|
|
if (!aOffset) return PR_TRUE;
|
|
|
|
|
|
|
|
if (IsTextNode(aNode))
|
|
|
|
{
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> firstNode;
|
|
|
|
GetFirstEditableNode(aNode, &firstNode);
|
|
|
|
if (!firstNode) return PR_TRUE;
|
|
|
|
PRInt32 offset;
|
|
|
|
nsEditor::GetChildOffset(firstNode, aNode, offset);
|
|
|
|
if (offset < aOffset) return PR_FALSE;
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool nsHTMLEditor::IsAtEndOfNode(nsIDOMNode *aNode, PRInt32 aOffset)
|
|
|
|
{
|
|
|
|
if (!aNode) return PR_FALSE; // oops
|
|
|
|
PRUint32 len;
|
|
|
|
GetLengthOfDOMNode(aNode, len);
|
2000-03-28 04:34:26 +04:00
|
|
|
if (aOffset == (PRInt32)len) return PR_TRUE;
|
2000-03-24 03:26:47 +03:00
|
|
|
|
|
|
|
if (IsTextNode(aNode))
|
|
|
|
{
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> lastNode;
|
|
|
|
GetLastEditableNode(aNode, &lastNode);
|
|
|
|
if (!lastNode) return PR_TRUE;
|
|
|
|
PRInt32 offset;
|
|
|
|
nsEditor::GetChildOffset(lastNode, aNode, offset);
|
|
|
|
if (offset < aOffset) return PR_TRUE;
|
|
|
|
return PR_FALSE;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
1999-03-01 22:54:47 +03:00
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty,
|
1999-08-19 17:30:48 +04:00
|
|
|
const nsString *aAttribute,
|
|
|
|
const nsString *aValue,
|
|
|
|
PRBool &aFirst, PRBool &aAny, PRBool &aAll)
|
1999-03-01 22:54:47 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
if (!aProperty)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
/*
|
|
|
|
if (gNoisy)
|
|
|
|
{
|
|
|
|
nsAutoString propString;
|
|
|
|
aProperty->ToString(propString);
|
|
|
|
char *propCString = propString.ToNewCString();
|
|
|
|
if (gNoisy) { printf("nsTextEditor::GetTextProperty %s\n", propCString); }
|
1999-09-06 10:22:51 +04:00
|
|
|
nsCRT::free(propCString);
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
*/
|
1999-08-19 17:30:48 +04:00
|
|
|
nsresult result;
|
1999-08-09 05:37:50 +04:00
|
|
|
aAny=PR_FALSE;
|
|
|
|
aAll=PR_TRUE;
|
|
|
|
aFirst=PR_FALSE;
|
|
|
|
PRBool first=PR_TRUE;
|
|
|
|
nsCOMPtr<nsIDOMSelection>selection;
|
1999-08-19 17:30:48 +04:00
|
|
|
result = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
if (!selection) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
PRBool isCollapsed;
|
|
|
|
selection->GetIsCollapsed(&isCollapsed);
|
2000-01-31 13:30:12 +03:00
|
|
|
nsCOMPtr<nsIDOMNode> collapsedNode;
|
1999-08-19 17:30:48 +04:00
|
|
|
nsCOMPtr<nsIEnumerator> enumerator;
|
|
|
|
result = selection->GetEnumerator(getter_AddRefs(enumerator));
|
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
if (!enumerator) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
enumerator->First();
|
|
|
|
nsCOMPtr<nsISupports> currentItem;
|
|
|
|
result = enumerator->CurrentItem(getter_AddRefs(currentItem));
|
|
|
|
// XXX: should be a while loop, to get each separate range
|
1999-09-30 00:08:15 +04:00
|
|
|
// XXX: ERROR_HANDLING can currentItem be null?
|
1999-08-19 17:30:48 +04:00
|
|
|
if ((NS_SUCCEEDED(result)) && currentItem)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
1999-08-19 17:30:48 +04:00
|
|
|
PRBool firstNodeInRange = PR_TRUE; // for each range, set a flag
|
2000-01-31 13:30:12 +03:00
|
|
|
nsCOMPtr<nsIDOMRange> range(do_QueryInterface(currentItem));
|
|
|
|
|
|
|
|
if (isCollapsed)
|
|
|
|
{
|
|
|
|
// efficiency hack. we cache prior results for being collapsed in a given text node.
|
|
|
|
// this speeds up typing. Note that other parts of the editor code have to clear out
|
|
|
|
// this cache after certain actions.
|
|
|
|
range->GetStartParent(getter_AddRefs(collapsedNode));
|
|
|
|
if (!collapsedNode) return NS_ERROR_FAILURE;
|
|
|
|
// refresh the cache if we need to
|
|
|
|
if (collapsedNode != mCachedNode) CacheInlineStyles(collapsedNode);
|
2000-02-03 01:47:43 +03:00
|
|
|
// cache now current, use it! But override it with typeInState results if any...
|
|
|
|
PRBool isSet, theSetting;
|
2000-01-31 13:30:12 +03:00
|
|
|
if (aProperty == mBoldAtom.get())
|
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
mTypeInState->GetTypingState(isSet, theSetting, aProperty);
|
2000-02-03 01:47:43 +03:00
|
|
|
if (isSet)
|
|
|
|
{
|
|
|
|
aFirst = aAny = aAll = theSetting;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
aFirst = aAny = aAll = mCachedBoldStyle;
|
|
|
|
}
|
2000-01-31 13:30:12 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
else if (aProperty == mItalicAtom.get())
|
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
mTypeInState->GetTypingState(isSet, theSetting, aProperty);
|
2000-02-03 01:47:43 +03:00
|
|
|
if (isSet)
|
|
|
|
{
|
|
|
|
aFirst = aAny = aAll = theSetting;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
aFirst = aAny = aAll = mCachedItalicStyle;
|
|
|
|
}
|
2000-01-31 13:30:12 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
else if (aProperty == mUnderlineAtom.get())
|
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
mTypeInState->GetTypingState(isSet, theSetting, aProperty);
|
2000-02-03 01:47:43 +03:00
|
|
|
if (isSet)
|
|
|
|
{
|
|
|
|
aFirst = aAny = aAll = theSetting;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
aFirst = aAny = aAll = mCachedUnderlineStyle;
|
|
|
|
}
|
2000-01-31 13:30:12 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
else if (aProperty == mFontAtom.get())
|
|
|
|
{
|
|
|
|
// MOOSE!
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// either non-collapsed selection or no cached value: do it the hard way
|
1999-08-19 17:30:48 +04:00
|
|
|
nsCOMPtr<nsIContentIterator> iter;
|
|
|
|
result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull,
|
2000-02-01 17:26:27 +03:00
|
|
|
NS_GET_IID(nsIContentIterator),
|
1999-08-19 17:30:48 +04:00
|
|
|
getter_AddRefs(iter));
|
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
if (!iter) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
iter->Init(range);
|
|
|
|
nsCOMPtr<nsIContent> content;
|
|
|
|
result = iter->CurrentNode(getter_AddRefs(content));
|
1999-10-28 07:16:48 +04:00
|
|
|
while (NS_ENUMERATOR_FALSE == iter->IsDone())
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
1999-08-19 17:30:48 +04:00
|
|
|
//if (gNoisy) { printf(" checking node %p\n", content.get()); }
|
|
|
|
nsCOMPtr<nsIDOMCharacterData>text;
|
|
|
|
text = do_QueryInterface(content);
|
|
|
|
PRBool skipNode = PR_FALSE;
|
|
|
|
if (text)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
if (!isCollapsed && first && firstNodeInRange)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
1999-08-19 17:30:48 +04:00
|
|
|
firstNodeInRange = PR_FALSE;
|
|
|
|
PRInt32 startOffset;
|
|
|
|
range->GetStartOffset(&startOffset);
|
|
|
|
PRUint32 count;
|
|
|
|
text->GetLength(&count);
|
|
|
|
if (startOffset==(PRInt32)count)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
1999-08-19 17:30:48 +04:00
|
|
|
//if (gNoisy) { printf(" skipping node %p\n", content.get()); }
|
|
|
|
skipNode = PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // handle non-text leaf nodes here
|
|
|
|
PRBool canContainChildren;
|
|
|
|
content->CanContainChildren(canContainChildren);
|
2000-03-24 03:26:47 +03:00
|
|
|
if (canContainChildren)
|
1999-08-19 17:30:48 +04:00
|
|
|
{
|
|
|
|
//if (gNoisy) { printf(" skipping non-leaf node %p\n", content.get()); }
|
|
|
|
skipNode = PR_TRUE;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
//if (gNoisy) { printf(" testing non-text leaf node %p\n", content.get()); }
|
|
|
|
}
|
|
|
|
}
|
2000-03-24 03:26:47 +03:00
|
|
|
if (!skipNode)
|
1999-08-19 17:30:48 +04:00
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode>node;
|
|
|
|
node = do_QueryInterface(content);
|
|
|
|
if (node)
|
|
|
|
{
|
|
|
|
PRBool isSet;
|
|
|
|
nsCOMPtr<nsIDOMNode>resultNode;
|
|
|
|
IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet, getter_AddRefs(resultNode));
|
2000-03-24 03:26:47 +03:00
|
|
|
if (first)
|
1999-08-19 17:30:48 +04:00
|
|
|
{
|
|
|
|
aFirst = isSet;
|
|
|
|
first = PR_FALSE;
|
|
|
|
}
|
2000-03-24 03:26:47 +03:00
|
|
|
if (isSet) {
|
1999-08-19 17:30:48 +04:00
|
|
|
aAny = PR_TRUE;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
aAll = PR_FALSE;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1999-08-19 17:30:48 +04:00
|
|
|
iter->Next();
|
|
|
|
iter->CurrentNode(getter_AddRefs(content));
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
}
|
2000-03-24 03:26:47 +03:00
|
|
|
if (!aAny)
|
2000-01-31 13:30:12 +03:00
|
|
|
{ // make sure that if none of the selection is set, we don't report all is set
|
1999-08-09 05:37:50 +04:00
|
|
|
aAll = PR_FALSE;
|
|
|
|
}
|
|
|
|
//if (gNoisy) { printf(" returning first=%d any=%d all=%d\n", aFirst, aAny, aAll); }
|
|
|
|
return result;
|
1999-03-01 22:54:47 +03:00
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP nsHTMLEditor::RemoveInlineProperty(nsIAtom *aProperty, const nsString *aAttribute)
|
1999-05-27 01:40:51 +04:00
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
if (!aProperty) return NS_ERROR_NULL_POINTER;
|
|
|
|
if (!mRules) return NS_ERROR_NOT_INITIALIZED;
|
1999-12-08 06:39:36 +03:00
|
|
|
ForceCompositionEnd();
|
1999-08-09 05:37:50 +04:00
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
nsresult res;
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMSelection>selection;
|
2000-03-24 03:26:47 +03:00
|
|
|
res = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(res)) return res;
|
1999-08-19 17:30:48 +04:00
|
|
|
if (!selection) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
PRBool isCollapsed;
|
|
|
|
selection->GetIsCollapsed(&isCollapsed);
|
|
|
|
if (isCollapsed)
|
|
|
|
{
|
|
|
|
// manipulating text attributes on a collapsed selection only sets state for the next text insertion
|
|
|
|
return mTypeInState->ClearProp(aProperty, *aAttribute);
|
|
|
|
}
|
2000-03-24 03:26:47 +03:00
|
|
|
nsAutoEditBatch batchIt(this);
|
|
|
|
nsAutoRules beginRulesSniffing(this, kOpRemoveTextProperty, nsIEditor::eNext);
|
|
|
|
nsAutoSelectionReset selectionResetter(selection, this);
|
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
PRBool cancel, handled;
|
1999-08-19 17:30:48 +04:00
|
|
|
nsTextRulesInfo ruleInfo(nsTextEditRules::kRemoveTextProperty);
|
2000-03-24 03:26:47 +03:00
|
|
|
res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
|
|
|
|
if (NS_FAILED(res)) return res;
|
1999-10-06 23:34:09 +04:00
|
|
|
if (!cancel && !handled)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
// get selection range enumerator
|
|
|
|
nsCOMPtr<nsIEnumerator> enumerator;
|
|
|
|
res = selection->GetEnumerator(getter_AddRefs(enumerator));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!enumerator) return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
// loop thru the ranges in the selection
|
|
|
|
enumerator->First();
|
|
|
|
nsCOMPtr<nsISupports> currentItem;
|
|
|
|
while ((NS_ENUMERATOR_FALSE == enumerator->IsDone()))
|
1999-08-19 17:30:48 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
res = enumerator->CurrentItem(getter_AddRefs(currentItem));
|
2000-03-24 03:26:47 +03:00
|
|
|
if (NS_FAILED(res)) return res;
|
2000-03-29 16:53:23 +04:00
|
|
|
if (!currentItem) return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
|
1999-08-19 17:30:48 +04:00
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
// adjust range to include any ancestors who's children are entirely selected
|
|
|
|
res = PromoteInlineRange(range);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
// remove this style from ancestors of our range empoints,
|
|
|
|
// splitting them as appropriate
|
|
|
|
res = SplitStyleAboveRange(range, aProperty, aAttribute);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
// check for easy case: both range endpoints in same text node
|
|
|
|
nsCOMPtr<nsIDOMNode> startNode, endNode;
|
|
|
|
res = range->GetStartParent(getter_AddRefs(startNode));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
res = range->GetEndParent(getter_AddRefs(endNode));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if ((startNode == endNode) && IsTextNode(startNode))
|
|
|
|
{
|
|
|
|
// we're done with this range!
|
|
|
|
}
|
|
|
|
else
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
// not the easy case. range not contained in single text node.
|
|
|
|
nsCOMPtr<nsIContentIterator> iter;
|
|
|
|
res = nsComponentManager::CreateInstance(kSubtreeIteratorCID, nsnull,
|
|
|
|
NS_GET_IID(nsIContentIterator),
|
|
|
|
getter_AddRefs(iter));
|
2000-03-24 03:26:47 +03:00
|
|
|
if (NS_FAILED(res)) return res;
|
2000-03-29 16:53:23 +04:00
|
|
|
if (!iter) return NS_ERROR_FAILURE;
|
1999-08-19 17:30:48 +04:00
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
nsCOMPtr<nsISupportsArray> arrayOfNodes;
|
|
|
|
nsCOMPtr<nsIContent> content;
|
|
|
|
nsCOMPtr<nsIDOMNode> node;
|
|
|
|
nsCOMPtr<nsISupports> isupports;
|
2000-03-24 03:26:47 +03:00
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
// make a array
|
|
|
|
res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes));
|
2000-03-24 03:26:47 +03:00
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
// iterate range and build up array
|
|
|
|
iter->Init(range);
|
|
|
|
while (NS_ENUMERATOR_FALSE == iter->IsDone())
|
2000-03-24 03:26:47 +03:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
res = iter->CurrentNode(getter_AddRefs(content));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
node = do_QueryInterface(content);
|
|
|
|
if (!node) return NS_ERROR_FAILURE;
|
|
|
|
if (IsEditable(node))
|
|
|
|
{
|
|
|
|
isupports = do_QueryInterface(node);
|
|
|
|
arrayOfNodes->AppendElement(isupports);
|
|
|
|
}
|
|
|
|
res = iter->Next();
|
|
|
|
if (NS_FAILED(res)) return res;
|
1999-08-19 17:30:48 +04:00
|
|
|
}
|
2000-03-29 16:53:23 +04:00
|
|
|
|
|
|
|
// loop through the list, remove the property on each node
|
|
|
|
PRUint32 listCount;
|
|
|
|
PRUint32 j;
|
|
|
|
arrayOfNodes->Count(&listCount);
|
|
|
|
for (j = 0; j < listCount; j++)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0));
|
|
|
|
node = do_QueryInterface(isupports);
|
|
|
|
res = RemoveStyleInside(node, aProperty, aAttribute);
|
2000-03-24 03:26:47 +03:00
|
|
|
if (NS_FAILED(res)) return res;
|
2000-03-29 16:53:23 +04:00
|
|
|
arrayOfNodes->RemoveElementAt(0);
|
2000-03-24 03:26:47 +03:00
|
|
|
}
|
1999-08-19 17:30:48 +04:00
|
|
|
}
|
2000-03-29 16:53:23 +04:00
|
|
|
enumerator->Next();
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
1999-10-06 23:34:09 +04:00
|
|
|
}
|
|
|
|
if (!cancel)
|
|
|
|
{
|
1999-08-19 17:30:48 +04:00
|
|
|
// post-process
|
2000-03-24 03:26:47 +03:00
|
|
|
res = mRules->DidDoAction(selection, &ruleInfo, res);
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
2000-03-24 03:26:47 +03:00
|
|
|
return res;
|
1999-05-27 01:40:51 +04:00
|
|
|
}
|
|
|
|
|
1999-11-13 19:37:58 +03:00
|
|
|
NS_IMETHODIMP nsHTMLEditor::IncreaseFontSize()
|
|
|
|
{
|
2000-02-08 15:53:34 +03:00
|
|
|
return RelativeFontChange(1);
|
1999-11-13 19:37:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsHTMLEditor::DecreaseFontSize()
|
|
|
|
{
|
2000-02-08 15:53:34 +03:00
|
|
|
return RelativeFontChange(-1);
|
1999-11-13 19:37:58 +03:00
|
|
|
}
|
1999-09-22 05:21:56 +04:00
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
nsresult nsHTMLEditor::GetTextSelectionOffsets(nsIDOMSelection *aSelection,
|
|
|
|
PRInt32 &aOutStartOffset,
|
|
|
|
PRInt32 &aOutEndOffset)
|
1999-09-22 05:21:56 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
if(!aSelection) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
nsresult result;
|
|
|
|
// initialize out params
|
|
|
|
aOutStartOffset = 0; // default to first char in selection
|
|
|
|
aOutEndOffset = -1; // default to total length of text in selection
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNode> startNode, endNode, parentNode;
|
|
|
|
PRInt32 startOffset, endOffset;
|
|
|
|
aSelection->GetAnchorNode(getter_AddRefs(startNode));
|
|
|
|
aSelection->GetAnchorOffset(&startOffset);
|
|
|
|
aSelection->GetFocusNode(getter_AddRefs(endNode));
|
|
|
|
aSelection->GetFocusOffset(&endOffset);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIEnumerator> enumerator;
|
|
|
|
result = aSelection->GetEnumerator(getter_AddRefs(enumerator));
|
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
if (!enumerator) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
// don't use "result" in this block
|
|
|
|
enumerator->First();
|
|
|
|
nsCOMPtr<nsISupports> currentItem;
|
|
|
|
nsresult findParentResult = enumerator->CurrentItem(getter_AddRefs(currentItem));
|
|
|
|
if ((NS_SUCCEEDED(findParentResult)) && (currentItem))
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
|
|
|
|
range->GetCommonParent(getter_AddRefs(parentNode));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
parentNode = do_QueryInterface(startNode);
|
|
|
|
}
|
|
|
|
|
1999-09-22 05:21:56 +04:00
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
return GetAbsoluteOffsetsForPoints(startNode, startOffset,
|
|
|
|
endNode, endOffset,
|
|
|
|
parentNode,
|
|
|
|
aOutStartOffset, aOutEndOffset);
|
|
|
|
}
|
1999-09-22 05:21:56 +04:00
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
// this is a complete ripoff from nsTextEditor::GetTextSelectionOffsetsForRange
|
|
|
|
// the two should use common code, or even just be one method
|
|
|
|
nsresult nsHTMLEditor::GetAbsoluteOffsetsForPoints(nsIDOMNode *aInStartNode,
|
|
|
|
PRInt32 aInStartOffset,
|
|
|
|
nsIDOMNode *aInEndNode,
|
|
|
|
PRInt32 aInEndOffset,
|
|
|
|
nsIDOMNode *aInCommonParentNode,
|
|
|
|
PRInt32 &aOutStartOffset,
|
|
|
|
PRInt32 &aOutEndOffset)
|
1999-09-22 05:21:56 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
if(!aInStartNode || !aInEndNode || !aInCommonParentNode) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
|
|
|
|
nsresult result;
|
|
|
|
// initialize out params
|
|
|
|
aOutStartOffset = 0; // default to first char in selection
|
|
|
|
aOutEndOffset = -1; // default to total length of text in selection
|
|
|
|
|
|
|
|
nsCOMPtr<nsIContentIterator> iter;
|
|
|
|
result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull,
|
|
|
|
NS_GET_IID(nsIContentIterator),
|
|
|
|
getter_AddRefs(iter));
|
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
if (!iter) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
PRUint32 totalLength=0;
|
|
|
|
nsCOMPtr<nsIDOMCharacterData>textNode;
|
|
|
|
nsCOMPtr<nsIContent>blockParentContent = do_QueryInterface(aInCommonParentNode);
|
|
|
|
iter->Init(blockParentContent);
|
|
|
|
// loop through the content iterator for each content node
|
|
|
|
nsCOMPtr<nsIContent> content;
|
|
|
|
result = iter->CurrentNode(getter_AddRefs(content));
|
|
|
|
while (NS_ENUMERATOR_FALSE == iter->IsDone())
|
|
|
|
{
|
|
|
|
textNode = do_QueryInterface(content);
|
|
|
|
if (textNode)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode>currentNode = do_QueryInterface(textNode);
|
|
|
|
if (!currentNode) {return NS_ERROR_NO_INTERFACE;}
|
|
|
|
if (IsEditable(currentNode))
|
|
|
|
{
|
|
|
|
if (currentNode.get() == aInStartNode)
|
|
|
|
{
|
|
|
|
aOutStartOffset = totalLength + aInStartOffset;
|
|
|
|
}
|
|
|
|
if (currentNode.get() == aInEndNode)
|
|
|
|
{
|
|
|
|
aOutEndOffset = totalLength + aInEndOffset;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
PRUint32 length;
|
|
|
|
textNode->GetLength(&length);
|
|
|
|
totalLength += length;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
iter->Next();
|
|
|
|
iter->CurrentNode(getter_AddRefs(content));
|
|
|
|
}
|
|
|
|
if (-1==aOutEndOffset) {
|
|
|
|
aOutEndOffset = totalLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
// guarantee that aOutStartOffset <= aOutEndOffset
|
|
|
|
if (aOutEndOffset<aOutStartOffset)
|
|
|
|
{
|
|
|
|
PRInt32 temp;
|
|
|
|
temp = aOutStartOffset;
|
|
|
|
aOutStartOffset= aOutEndOffset;
|
|
|
|
aOutEndOffset = temp;
|
|
|
|
}
|
|
|
|
NS_POSTCONDITION(aOutStartOffset <= aOutEndOffset, "start > end");
|
|
|
|
return result;
|
1999-09-22 05:21:56 +04:00
|
|
|
}
|
|
|
|
|
2000-04-04 18:51:26 +04:00
|
|
|
nsresult
|
|
|
|
nsHTMLEditor::CollapseSelectionToDeepestNonTableFirstChild(nsIDOMSelection *aSelection, nsIDOMNode *aNode)
|
|
|
|
{
|
|
|
|
if (!aNode) return NS_ERROR_NULL_POINTER;
|
|
|
|
nsresult res;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
|
|
|
if (aSelection)
|
|
|
|
{
|
|
|
|
selection = aSelection;
|
|
|
|
} else {
|
|
|
|
res = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!selection) return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
nsCOMPtr<nsIDOMNode> node = aNode;
|
|
|
|
nsCOMPtr<nsIDOMNode> child;
|
|
|
|
|
|
|
|
do {
|
|
|
|
node->GetFirstChild(getter_AddRefs(child));
|
|
|
|
|
|
|
|
if (child)
|
|
|
|
{
|
|
|
|
// Stop if we find a table
|
|
|
|
// don't want to go into nested tables
|
|
|
|
if (IsTable(child)) break;
|
|
|
|
node = child;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (child);
|
|
|
|
|
|
|
|
selection->Collapse(node,0);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-12-07 11:30:19 +03:00
|
|
|
NS_IMETHODIMP nsHTMLEditor::DeleteSelection(nsIEditor::EDirection aAction)
|
1999-05-27 01:40:51 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
|
1999-05-27 01:40:51 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
1999-10-06 23:34:09 +04:00
|
|
|
PRBool cancel, handled;
|
2000-03-14 05:58:14 +03:00
|
|
|
nsresult result;
|
1999-05-27 01:40:51 +04:00
|
|
|
|
1999-12-15 02:07:12 +03:00
|
|
|
// If it's one of these modes,
|
|
|
|
// we have to extend the selection first.
|
|
|
|
// This can't happen inside selection batching --
|
|
|
|
// selection refuses to move if batching is on.
|
|
|
|
if (aAction == eNextWord || aAction == ePreviousWord
|
2000-01-04 23:38:12 +03:00
|
|
|
|| aAction == eToBeginningOfLine || aAction == eToEndOfLine)
|
1999-12-15 02:07:12 +03:00
|
|
|
{
|
|
|
|
if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
|
|
|
|
nsCOMPtr<nsISelectionController> selCont (do_QueryInterface(ps));
|
|
|
|
if (!selCont)
|
|
|
|
return NS_ERROR_NO_INTERFACE;
|
|
|
|
|
|
|
|
switch (aAction)
|
|
|
|
{
|
|
|
|
case eNextWord:
|
|
|
|
result = selCont->WordMove(PR_TRUE, PR_TRUE);
|
|
|
|
// DeleteSelectionImpl doesn't handle these actions
|
|
|
|
// because it's inside batching, so don't confuse it:
|
|
|
|
aAction = eNone;
|
|
|
|
break;
|
|
|
|
case ePreviousWord:
|
|
|
|
result = selCont->WordMove(PR_FALSE, PR_TRUE);
|
|
|
|
aAction = eNone;
|
|
|
|
break;
|
2000-01-04 23:38:12 +03:00
|
|
|
case eToBeginningOfLine:
|
2000-03-14 05:58:14 +03:00
|
|
|
selCont->IntraLineMove(PR_TRUE, PR_FALSE); // try to move to end
|
|
|
|
result = selCont->IntraLineMove(PR_FALSE, PR_TRUE); // select to beginning
|
2000-01-04 23:38:12 +03:00
|
|
|
aAction = eNone;
|
|
|
|
break;
|
1999-12-15 02:07:12 +03:00
|
|
|
case eToEndOfLine:
|
|
|
|
result = selCont->IntraLineMove(PR_TRUE, PR_TRUE);
|
2000-03-14 05:58:14 +03:00
|
|
|
aAction = eNext;
|
1999-12-15 02:07:12 +03:00
|
|
|
break;
|
2000-01-07 01:35:04 +03:00
|
|
|
default: // avoid several compiler warnings
|
|
|
|
result = NS_OK;
|
|
|
|
break;
|
1999-12-15 02:07:12 +03:00
|
|
|
}
|
|
|
|
if (NS_FAILED(result))
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
printf("Selection controller interface didn't work!\n");
|
|
|
|
#endif
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
// delete placeholder txns merge.
|
|
|
|
nsAutoPlaceHolderBatch batch(this, gDeleteTxnName);
|
1999-12-07 11:30:19 +03:00
|
|
|
nsAutoRules beginRulesSniffing(this, kOpDeleteSelection, aAction);
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
// pre-process
|
2000-03-14 05:58:14 +03:00
|
|
|
result = GetSelection(getter_AddRefs(selection));
|
1999-08-19 17:30:48 +04:00
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
if (!selection) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsTextRulesInfo ruleInfo(nsTextEditRules::kDeleteSelection);
|
|
|
|
ruleInfo.collapsedAction = aAction;
|
1999-10-06 23:34:09 +04:00
|
|
|
result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
|
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
if (!cancel && !handled)
|
1999-05-27 01:40:51 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
result = DeleteSelectionImpl(aAction);
|
1999-10-06 23:34:09 +04:00
|
|
|
}
|
|
|
|
if (!cancel)
|
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
// post-process
|
|
|
|
result = mRules->DidDoAction(selection, &ruleInfo, result);
|
1999-05-27 01:40:51 +04:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
return result;
|
1999-05-27 01:40:51 +04:00
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP nsHTMLEditor::InsertText(const nsString& aStringToInsert)
|
1999-03-01 22:54:47 +03:00
|
|
|
{
|
1999-03-29 12:02:05 +04:00
|
|
|
if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
1999-10-06 23:34:09 +04:00
|
|
|
PRBool cancel, handled;
|
1999-12-07 11:30:19 +03:00
|
|
|
PRInt32 theAction = nsTextEditRules::kInsertText;
|
|
|
|
PRInt32 opID = kOpInsertText;
|
|
|
|
if (mInIMEMode)
|
|
|
|
{
|
|
|
|
theAction = nsTextEditRules::kInsertTextIME;
|
|
|
|
opID = kOpInsertIMEText;
|
|
|
|
}
|
1999-10-06 23:34:09 +04:00
|
|
|
nsAutoPlaceHolderBatch batch(this, nsnull);
|
1999-12-07 11:30:19 +03:00
|
|
|
nsAutoRules beginRulesSniffing(this, opID, nsIEditor::eNext);
|
1999-03-29 12:02:05 +04:00
|
|
|
|
|
|
|
// pre-process
|
1999-08-19 17:30:48 +04:00
|
|
|
nsresult result = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
if (!selection) return NS_ERROR_NULL_POINTER;
|
1999-09-15 03:40:16 +04:00
|
|
|
nsAutoString resultString;
|
1999-12-07 11:30:19 +03:00
|
|
|
nsTextRulesInfo ruleInfo(theAction);
|
1999-08-09 05:37:50 +04:00
|
|
|
ruleInfo.inString = &aStringToInsert;
|
|
|
|
ruleInfo.outString = &resultString;
|
|
|
|
ruleInfo.maxLength = mMaxTextLength;
|
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
|
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
if (!cancel && !handled)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
// we rely on rules code for now - no default implementation
|
1999-10-06 23:34:09 +04:00
|
|
|
}
|
|
|
|
if (!cancel)
|
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
// post-process
|
|
|
|
result = mRules->DidDoAction(selection, &ruleInfo, result);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsHTMLEditor::InsertHTML(const nsString& aInputString)
|
2000-01-04 23:38:12 +03:00
|
|
|
{
|
|
|
|
nsAutoString charset;
|
|
|
|
return InsertHTMLWithCharset(aInputString, charset);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsHTMLEditor::InsertHTMLWithCharset(const nsString& aInputString,
|
|
|
|
const nsString& aCharset)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
1999-12-08 06:39:36 +03:00
|
|
|
ForceCompositionEnd();
|
1999-08-09 05:37:50 +04:00
|
|
|
nsAutoEditBatch beginBatching(this);
|
1999-12-07 11:30:19 +03:00
|
|
|
nsAutoRules beginRulesSniffing(this, kOpInsertElement, nsIEditor::eNext);
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsresult res;
|
1999-10-22 01:51:47 +04:00
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMSelection>selection;
|
|
|
|
|
|
|
|
if (!mRules) return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
|
|
|
|
res = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> parentNode;
|
|
|
|
PRInt32 offsetOfNewNode;
|
|
|
|
res = DeleteSelectionAndPrepareToCreateNode(parentNode, offsetOfNewNode);
|
1999-11-29 11:28:46 +03:00
|
|
|
if (NS_FAILED(res)) return res;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
res = GetSelection(getter_AddRefs(selection));
|
1999-08-19 17:30:48 +04:00
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!selection) return NS_ERROR_NULL_POINTER;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-11-29 11:28:46 +03:00
|
|
|
// give rules a chance to handle or cancel
|
|
|
|
nsTextRulesInfo ruleInfo(nsHTMLEditRules::kInsertElement);
|
|
|
|
PRBool cancel, handled;
|
|
|
|
res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (cancel) return NS_OK; // rules canceled the operation
|
|
|
|
if (!handled)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
1999-11-29 11:28:46 +03:00
|
|
|
// Get the first range in the selection, for context:
|
|
|
|
nsCOMPtr<nsIDOMRange> range;
|
|
|
|
res = selection->GetRangeAt(0, getter_AddRefs(range));
|
|
|
|
if (NS_FAILED(res))
|
|
|
|
return res;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNSRange> nsrange (do_QueryInterface(range));
|
|
|
|
if (!nsrange)
|
|
|
|
return NS_ERROR_NO_INTERFACE;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMDocumentFragment> docfrag;
|
|
|
|
res = nsrange->CreateContextualFragment(aInputString,
|
|
|
|
getter_AddRefs(docfrag));
|
|
|
|
if (NS_FAILED(res))
|
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
#ifdef DEBUG
|
1999-11-29 11:28:46 +03:00
|
|
|
printf("Couldn't create contextual fragment: error was %d\n", res);
|
1999-08-09 05:37:50 +04:00
|
|
|
#endif
|
1999-11-29 11:28:46 +03:00
|
|
|
return res;
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-08-20 02:11:58 +04:00
|
|
|
#if defined(DEBUG_akkana_verbose)
|
1999-11-29 11:28:46 +03:00
|
|
|
printf("============ Fragment dump :===========\n");
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-11-29 11:28:46 +03:00
|
|
|
nsCOMPtr<nsIContent> fragc (do_QueryInterface(docfrag));
|
|
|
|
if (!fragc)
|
|
|
|
printf("Couldn't get fragment is nsIContent\n");
|
|
|
|
else
|
|
|
|
fragc->List(stdout);
|
1999-08-09 05:37:50 +04:00
|
|
|
#endif
|
|
|
|
|
1999-11-29 11:28:46 +03:00
|
|
|
// Insert the contents of the document fragment:
|
|
|
|
nsCOMPtr<nsIDOMNode> fragmentAsNode (do_QueryInterface(docfrag));
|
1999-09-02 21:56:09 +04:00
|
|
|
|
1999-11-29 11:28:46 +03:00
|
|
|
// Loop over the contents of the fragment:
|
|
|
|
nsCOMPtr<nsIDOMNode> child;
|
|
|
|
res = fragmentAsNode->GetFirstChild(getter_AddRefs(child));
|
|
|
|
if (NS_FAILED(res))
|
|
|
|
{
|
|
|
|
printf("GetFirstChild failed!\n");
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
while (child)
|
|
|
|
{
|
1999-09-02 21:56:09 +04:00
|
|
|
#if defined(DEBUG_akkana_verbose)
|
1999-11-29 11:28:46 +03:00
|
|
|
printf("About to try to insert this node:\n");
|
|
|
|
nsCOMPtr<nsIContent> nodec (do_QueryInterface(child));
|
|
|
|
if (nodec) nodec->List(stdout);
|
|
|
|
printf("-----\n");
|
1999-09-02 21:56:09 +04:00
|
|
|
#endif
|
1999-11-29 11:28:46 +03:00
|
|
|
// Get the next sibling before inserting the node;
|
|
|
|
// when we insert the node, it moves into the main doc tree
|
|
|
|
// so we'll no longer be able to get the siblings in the doc frag.
|
|
|
|
nsCOMPtr<nsIDOMNode> nextSib;
|
|
|
|
child->GetNextSibling(getter_AddRefs(nextSib));
|
|
|
|
// Ignore the return value, we'll check child when we loop around again.
|
|
|
|
|
|
|
|
// Now we can insert the node.
|
|
|
|
res = InsertNode(child, parentNode, offsetOfNewNode++);
|
|
|
|
if (NS_FAILED(res))
|
|
|
|
break;
|
|
|
|
child = nextSib;
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
if (NS_FAILED(res))
|
1999-11-29 11:28:46 +03:00
|
|
|
return res;
|
1999-09-02 21:56:09 +04:00
|
|
|
|
1999-11-29 11:28:46 +03:00
|
|
|
// Now collapse the selection to the end of what we just inserted:
|
|
|
|
selection->Collapse(parentNode, offsetOfNewNode);
|
|
|
|
}
|
|
|
|
|
|
|
|
res = mRules->DidDoAction(selection, &ruleInfo, res);
|
1999-08-09 05:37:50 +04:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsHTMLEditor::InsertBreak()
|
|
|
|
{
|
|
|
|
nsresult res;
|
|
|
|
if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
|
|
|
|
|
1999-12-16 03:59:08 +03:00
|
|
|
nsAutoRules beginRulesSniffing(this, kOpInsertBreak, nsIEditor::eNext);
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
1999-10-06 23:34:09 +04:00
|
|
|
PRBool cancel, handled;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
// pre-process
|
1999-08-19 17:30:48 +04:00
|
|
|
res = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!selection) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsTextRulesInfo ruleInfo(nsHTMLEditRules::kInsertBreak);
|
1999-10-06 23:34:09 +04:00
|
|
|
res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!cancel && !handled)
|
1999-03-29 12:02:05 +04:00
|
|
|
{
|
|
|
|
// create the new BR node
|
|
|
|
nsCOMPtr<nsIDOMNode> newNode;
|
|
|
|
nsAutoString tag("BR");
|
1999-08-09 05:37:50 +04:00
|
|
|
res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode));
|
1999-08-19 17:30:48 +04:00
|
|
|
if (!newNode) res = NS_ERROR_NULL_POINTER; // don't return here, so DidDoAction is called
|
|
|
|
if (NS_SUCCEEDED(res))
|
1999-03-29 12:02:05 +04:00
|
|
|
{
|
|
|
|
// set the selection to the new node
|
|
|
|
nsCOMPtr<nsIDOMNode>parent;
|
1999-06-03 10:00:23 +04:00
|
|
|
res = newNode->GetParentNode(getter_AddRefs(parent));
|
1999-08-19 17:30:48 +04:00
|
|
|
if (!parent) res = NS_ERROR_NULL_POINTER; // don't return here, so DidDoAction is called
|
|
|
|
if (NS_SUCCEEDED(res))
|
1999-03-29 12:02:05 +04:00
|
|
|
{
|
|
|
|
PRInt32 offsetInParent=-1; // we use the -1 as a marker to see if we need to compute this or not
|
|
|
|
nsCOMPtr<nsIDOMNode>nextNode;
|
|
|
|
newNode->GetNextSibling(getter_AddRefs(nextNode));
|
|
|
|
if (nextNode)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMCharacterData>nextTextNode;
|
|
|
|
nextTextNode = do_QueryInterface(nextNode);
|
|
|
|
if (!nextTextNode) {
|
|
|
|
nextNode = do_QueryInterface(newNode);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
offsetInParent=0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
nextNode = do_QueryInterface(newNode);
|
|
|
|
}
|
1999-08-19 17:30:48 +04:00
|
|
|
res = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (!selection) res = NS_ERROR_NULL_POINTER; // don't return here, so DidDoAction is called
|
1999-06-03 10:00:23 +04:00
|
|
|
if (NS_SUCCEEDED(res))
|
1999-03-29 12:02:05 +04:00
|
|
|
{
|
|
|
|
if (-1==offsetInParent)
|
|
|
|
{
|
|
|
|
nextNode->GetParentNode(getter_AddRefs(parent));
|
1999-06-03 10:00:23 +04:00
|
|
|
res = GetChildOffset(nextNode, parent, offsetInParent);
|
|
|
|
if (NS_SUCCEEDED(res)) {
|
2000-02-25 07:39:30 +03:00
|
|
|
// SetHint(PR_TRUE) means we want the caret to stick to the content on the "right".
|
|
|
|
// We want the caret to stick to whatever is past the break. This is
|
|
|
|
// because the break is on the same line we were on, but the next content
|
|
|
|
// will be on the following line.
|
|
|
|
selection->SetHint(PR_TRUE);
|
1999-08-19 17:30:48 +04:00
|
|
|
res = selection->Collapse(parent, offsetInParent+1); // +1 to insert just after the break
|
1999-03-29 12:02:05 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
1999-08-19 17:30:48 +04:00
|
|
|
res = selection->Collapse(nextNode, offsetInParent);
|
1999-03-29 12:02:05 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1999-10-06 23:34:09 +04:00
|
|
|
}
|
|
|
|
if (!cancel)
|
|
|
|
{
|
1999-03-29 12:02:05 +04:00
|
|
|
// post-process, always called if WillInsertBreak didn't return cancel==PR_TRUE
|
1999-06-03 10:00:23 +04:00
|
|
|
res = mRules->DidDoAction(selection, &ruleInfo, res);
|
1999-03-29 12:02:05 +04:00
|
|
|
}
|
1999-07-28 01:18:10 +04:00
|
|
|
|
1999-06-03 10:00:23 +04:00
|
|
|
return res;
|
1999-03-01 22:54:47 +03:00
|
|
|
}
|
|
|
|
|
1999-05-06 03:29:18 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP
|
1999-10-14 04:13:27 +04:00
|
|
|
nsHTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSelection)
|
1999-05-06 03:29:18 +04:00
|
|
|
{
|
1999-06-03 10:00:23 +04:00
|
|
|
nsresult res = NS_ERROR_NOT_INITIALIZED;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
if (!aElement)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
1999-12-08 06:39:36 +03:00
|
|
|
ForceCompositionEnd();
|
1999-09-30 00:08:15 +04:00
|
|
|
nsAutoEditBatch beginBatching(this);
|
1999-12-07 11:30:19 +03:00
|
|
|
nsAutoRules beginRulesSniffing(this, kOpInsertElement, nsIEditor::eNext);
|
1999-09-30 00:08:15 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMSelection>selection;
|
1999-08-19 17:30:48 +04:00
|
|
|
res = GetSelection(getter_AddRefs(selection));
|
1999-08-09 05:37:50 +04:00
|
|
|
if (!NS_SUCCEEDED(res) || !selection)
|
|
|
|
return NS_ERROR_FAILURE;
|
1999-03-01 22:54:47 +03:00
|
|
|
|
1999-08-10 02:51:40 +04:00
|
|
|
// hand off to the rules system, see if it has anything to say about this
|
1999-10-06 23:34:09 +04:00
|
|
|
PRBool cancel, handled;
|
1999-08-10 02:51:40 +04:00
|
|
|
nsTextRulesInfo ruleInfo(nsHTMLEditRules::kInsertElement);
|
|
|
|
ruleInfo.insertElement = aElement;
|
1999-10-06 23:34:09 +04:00
|
|
|
res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
|
1999-08-10 02:51:40 +04:00
|
|
|
if (cancel || (NS_FAILED(res))) return res;
|
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
if (!handled)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
1999-10-06 23:34:09 +04:00
|
|
|
if (aDeleteSelection)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
1999-10-06 23:34:09 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> tempNode;
|
|
|
|
PRInt32 tempOffset;
|
|
|
|
nsresult result = DeleteSelectionAndPrepareToCreateNode(tempNode,tempOffset);
|
|
|
|
if (!NS_SUCCEEDED(result))
|
|
|
|
return result;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
1999-09-21 05:36:30 +04:00
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
// If deleting, selection will be collapsed.
|
|
|
|
// so if not, we collapse it
|
|
|
|
if (!aDeleteSelection)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
1999-10-06 23:34:09 +04:00
|
|
|
// Named Anchor is a special case,
|
|
|
|
// We collapse to insert element BEFORE the selection
|
|
|
|
// For all other tags, we insert AFTER the selection
|
|
|
|
nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aElement);
|
|
|
|
if (IsNamedAnchorNode(node))
|
|
|
|
{
|
|
|
|
selection->CollapseToStart();
|
|
|
|
} else {
|
|
|
|
selection->CollapseToEnd();
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
1999-03-01 22:54:47 +03:00
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> parentSelectedNode;
|
|
|
|
PRInt32 offsetForInsert;
|
|
|
|
res = selection->GetAnchorNode(getter_AddRefs(parentSelectedNode));
|
|
|
|
// XXX: ERROR_HANDLING bad XPCOM usage
|
|
|
|
if (NS_SUCCEEDED(res) && NS_SUCCEEDED(selection->GetAnchorOffset(&offsetForInsert)) && parentSelectedNode)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
|
|
|
#ifdef DEBUG_cmanske
|
1999-10-06 23:34:09 +04:00
|
|
|
{
|
|
|
|
nsAutoString name;
|
|
|
|
parentSelectedNode->GetNodeName(name);
|
|
|
|
printf("InsertElement: Anchor node of selection: ");
|
|
|
|
wprintf(name.GetUnicode());
|
|
|
|
printf(" Offset: %d\n", offsetForInsert);
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
#endif
|
1999-10-06 23:34:09 +04:00
|
|
|
nsAutoString tagName;
|
|
|
|
aElement->GetNodeName(tagName);
|
|
|
|
tagName.ToLowerCase();
|
|
|
|
nsCOMPtr<nsIDOMNode> parent = parentSelectedNode;
|
|
|
|
nsCOMPtr<nsIDOMNode> topChild = parentSelectedNode;
|
|
|
|
nsCOMPtr<nsIDOMNode> tmp;
|
|
|
|
nsAutoString parentTagName;
|
|
|
|
PRBool isRoot;
|
|
|
|
|
|
|
|
// Search up the parent chain to find a suitable container
|
|
|
|
while (!CanContainTag(parent, tagName))
|
|
|
|
{
|
|
|
|
// If the current parent is a root (body or table cell)
|
|
|
|
// then go no further - we can't insert
|
|
|
|
parent->GetNodeName(parentTagName);
|
|
|
|
res = IsRootTag(parentTagName, isRoot);
|
|
|
|
if (!NS_SUCCEEDED(res) || isRoot)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
// Get the next parent
|
|
|
|
parent->GetParentNode(getter_AddRefs(tmp));
|
|
|
|
if (!tmp)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
topChild = parent;
|
|
|
|
parent = tmp;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_cmanske
|
|
|
|
{
|
|
|
|
nsAutoString name;
|
|
|
|
parent->GetNodeName(name);
|
|
|
|
printf("Parent node to insert under: ");
|
|
|
|
wprintf(name.GetUnicode());
|
|
|
|
printf("\n");
|
|
|
|
topChild->GetNodeName(name);
|
|
|
|
printf("TopChild to split: ");
|
|
|
|
wprintf(name.GetUnicode());
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (parent != topChild)
|
|
|
|
{
|
|
|
|
// we need to split some levels above the original selection parent
|
|
|
|
res = SplitNodeDeep(topChild, parentSelectedNode, offsetForInsert, &offsetForInsert);
|
|
|
|
if (NS_FAILED(res))
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
// Now we can insert the new node
|
|
|
|
res = InsertNode(aElement, parent, offsetForInsert);
|
1999-08-10 02:51:40 +04:00
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
// Set caret after element, but check for special case
|
|
|
|
// of inserting table-related elements: set in first cell instead
|
|
|
|
if (!SetCaretInTableCell(aElement))
|
|
|
|
res = SetCaretAfterElement(aElement);
|
1999-03-01 22:54:47 +03:00
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
1999-10-06 23:34:09 +04:00
|
|
|
res = mRules->DidDoAction(selection, &ruleInfo, res);
|
1999-08-09 05:37:50 +04:00
|
|
|
return res;
|
1999-03-01 22:54:47 +03:00
|
|
|
}
|
|
|
|
|
1999-08-19 17:30:48 +04:00
|
|
|
// XXX: error handling in this routine needs to be cleaned up!
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::DeleteSelectionAndCreateNode(const nsString& aTag,
|
1999-08-19 17:30:48 +04:00
|
|
|
nsIDOMNode ** aNewNode)
|
1999-06-11 01:31:42 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> parentSelectedNode;
|
|
|
|
PRInt32 offsetOfNewNode;
|
|
|
|
nsresult result = DeleteSelectionAndPrepareToCreateNode(parentSelectedNode,
|
|
|
|
offsetOfNewNode);
|
|
|
|
if (!NS_SUCCEEDED(result))
|
|
|
|
return result;
|
1999-06-11 01:31:42 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> newNode;
|
|
|
|
result = CreateNode(aTag, parentSelectedNode, offsetOfNewNode,
|
|
|
|
getter_AddRefs(newNode));
|
1999-08-19 17:30:48 +04:00
|
|
|
// XXX: ERROR_HANDLING check result, and make sure aNewNode is set correctly in success/failure cases
|
1999-08-09 05:37:50 +04:00
|
|
|
*aNewNode = newNode;
|
1999-11-22 03:01:18 +03:00
|
|
|
NS_IF_ADDREF(*aNewNode);
|
1999-06-11 01:31:42 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
// we want the selection to be just after the new node
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
|
|
|
result = GetSelection(getter_AddRefs(selection));
|
1999-09-30 00:08:15 +04:00
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
if (!selection) return NS_ERROR_NULL_POINTER;
|
1999-08-19 17:30:48 +04:00
|
|
|
result = selection->Collapse(parentSelectedNode, offsetOfNewNode+1);
|
1999-03-01 22:54:47 +03:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
return result;
|
1999-03-01 22:54:47 +03:00
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::SelectElement(nsIDOMElement* aElement)
|
1999-03-01 22:54:47 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsresult res = NS_ERROR_NULL_POINTER;
|
1999-05-07 09:02:35 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
// Must be sure that element is contained in the document body
|
|
|
|
if (IsElementInBody(aElement))
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
1999-08-19 17:30:48 +04:00
|
|
|
res = GetSelection(getter_AddRefs(selection));
|
1999-09-30 00:08:15 +04:00
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!selection) return NS_ERROR_NULL_POINTER;
|
1999-08-19 17:30:48 +04:00
|
|
|
nsCOMPtr<nsIDOMNode>parent;
|
|
|
|
res = aElement->GetParentNode(getter_AddRefs(parent));
|
|
|
|
if (NS_SUCCEEDED(res) && parent)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
1999-08-19 17:30:48 +04:00
|
|
|
PRInt32 offsetInParent;
|
|
|
|
res = GetChildOffset(aElement, parent, offsetInParent);
|
1999-03-11 00:29:41 +03:00
|
|
|
|
1999-08-19 17:30:48 +04:00
|
|
|
if (NS_SUCCEEDED(res))
|
|
|
|
{
|
|
|
|
// Collapse selection to just before desired element,
|
|
|
|
res = selection->Collapse(parent, offsetInParent);
|
1999-09-30 00:08:15 +04:00
|
|
|
if (NS_SUCCEEDED(res)) {
|
|
|
|
// then extend it to just after
|
|
|
|
res = selection->Extend(parent, offsetInParent+1);
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res;
|
1999-03-11 00:29:41 +03:00
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::SetCaretAfterElement(nsIDOMElement* aElement)
|
1999-03-11 00:29:41 +03:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsresult res = NS_ERROR_NULL_POINTER;
|
1999-07-19 23:37:08 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
// Be sure the element is contained in the document body
|
|
|
|
if (aElement && IsElementInBody(aElement))
|
1999-07-19 23:37:08 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
1999-08-19 17:30:48 +04:00
|
|
|
res = GetSelection(getter_AddRefs(selection));
|
1999-09-30 00:08:15 +04:00
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!selection) return NS_ERROR_NULL_POINTER;
|
1999-08-19 17:30:48 +04:00
|
|
|
nsCOMPtr<nsIDOMNode>parent;
|
|
|
|
res = aElement->GetParentNode(getter_AddRefs(parent));
|
1999-09-30 00:08:15 +04:00
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!parent) return NS_ERROR_NULL_POINTER;
|
1999-08-19 17:30:48 +04:00
|
|
|
PRInt32 offsetInParent;
|
|
|
|
res = GetChildOffset(aElement, parent, offsetInParent);
|
|
|
|
if (NS_SUCCEEDED(res))
|
1999-07-19 23:37:08 +04:00
|
|
|
{
|
1999-08-19 17:30:48 +04:00
|
|
|
// Collapse selection to just after desired element,
|
|
|
|
res = selection->Collapse(parent, offsetInParent+1);
|
1999-08-27 08:12:47 +04:00
|
|
|
#if 0 //def DEBUG_cmanske
|
1999-08-19 17:30:48 +04:00
|
|
|
{
|
|
|
|
nsAutoString name;
|
|
|
|
parent->GetNodeName(name);
|
|
|
|
printf("SetCaretAfterElement: Parent node: ");
|
|
|
|
wprintf(name.GetUnicode());
|
|
|
|
printf(" Offset: %d\n\nHTML:\n", offsetInParent+1);
|
1999-09-15 03:40:16 +04:00
|
|
|
nsAutoString Format("text/html");
|
|
|
|
nsAutoString ContentsAs;
|
1999-08-19 17:30:48 +04:00
|
|
|
OutputToString(ContentsAs, Format, 2);
|
|
|
|
wprintf(ContentsAs.GetUnicode());
|
1999-07-19 23:37:08 +04:00
|
|
|
}
|
1999-08-19 17:30:48 +04:00
|
|
|
#endif
|
1999-07-19 23:37:08 +04:00
|
|
|
}
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
return res;
|
1999-03-11 00:29:41 +03:00
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP nsHTMLEditor::SetParagraphFormat(const nsString& aParagraphFormat)
|
1999-05-27 04:08:15 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
//Kinda sad to waste memory just to force lower case
|
|
|
|
nsAutoString tag = aParagraphFormat;
|
|
|
|
tag.ToLowerCase();
|
2000-01-15 17:29:29 +03:00
|
|
|
return InsertBasicBlock(tag);
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
1999-05-27 04:08:15 +04:00
|
|
|
|
1999-08-19 17:30:48 +04:00
|
|
|
// XXX: ERROR_HANDLING -- this method needs a little work to ensure all error codes are
|
|
|
|
// checked properly, all null pointers are checked, and no memory leaks occur
|
1999-04-20 21:47:12 +04:00
|
|
|
NS_IMETHODIMP
|
1999-09-22 09:52:44 +04:00
|
|
|
nsHTMLEditor::GetParentBlockTags(nsStringArray *aTagList, PRBool aGetLists)
|
1999-04-20 21:47:12 +04:00
|
|
|
{
|
1999-05-01 02:40:18 +04:00
|
|
|
if (!aTagList) { return NS_ERROR_NULL_POINTER; }
|
1999-04-20 21:47:12 +04:00
|
|
|
|
1999-06-03 10:00:23 +04:00
|
|
|
nsresult res;
|
1999-05-01 02:40:18 +04:00
|
|
|
nsCOMPtr<nsIDOMSelection>selection;
|
1999-08-19 17:30:48 +04:00
|
|
|
res = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!selection) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
2000-01-15 17:29:29 +03:00
|
|
|
// Find out if the selection is collapsed:
|
|
|
|
PRBool isCollapsed;
|
|
|
|
res = selection->GetIsCollapsed(&isCollapsed);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (isCollapsed)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> node, blockParent;
|
|
|
|
PRInt32 offset;
|
|
|
|
|
|
|
|
res = GetStartNodeAndOffset(selection, &node, &offset);
|
|
|
|
if (!node) res = NS_ERROR_FAILURE;
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMElement> blockParentElem;
|
|
|
|
if (aGetLists)
|
|
|
|
{
|
|
|
|
// Get the "ol", "ul", or "dl" parent element
|
|
|
|
res = GetElementOrParentByTagName("list", node, getter_AddRefs(blockParentElem));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (IsBlockNode(node)) blockParent = node;
|
|
|
|
else blockParent = GetBlockNodeParent(node);
|
|
|
|
blockParentElem = do_QueryInterface(blockParent);
|
|
|
|
}
|
|
|
|
if (blockParentElem)
|
|
|
|
{
|
|
|
|
nsAutoString blockParentTag;
|
|
|
|
blockParentElem->GetTagName(blockParentTag);
|
|
|
|
aTagList->AppendString(blockParentTag);
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
// else non-collapsed selection
|
1999-08-19 17:30:48 +04:00
|
|
|
nsCOMPtr<nsIEnumerator> enumerator;
|
|
|
|
res = selection->GetEnumerator(getter_AddRefs(enumerator));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!enumerator) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
enumerator->First();
|
|
|
|
nsCOMPtr<nsISupports> currentItem;
|
|
|
|
res = enumerator->CurrentItem(getter_AddRefs(currentItem));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
//XXX: should be while loop?
|
|
|
|
if (currentItem)
|
1999-05-01 02:40:18 +04:00
|
|
|
{
|
1999-08-19 17:30:48 +04:00
|
|
|
nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
|
|
|
|
// scan the range for all the independent block content blockSections
|
|
|
|
// and get the block parent of each
|
|
|
|
nsISupportsArray *blockSections;
|
|
|
|
res = NS_NewISupportsArray(&blockSections);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!blockSections) return NS_ERROR_NULL_POINTER;
|
|
|
|
res = GetBlockSectionsForRange(range, blockSections);
|
|
|
|
if (NS_SUCCEEDED(res))
|
1999-05-01 02:40:18 +04:00
|
|
|
{
|
1999-08-19 17:30:48 +04:00
|
|
|
nsIDOMRange *subRange;
|
|
|
|
subRange = (nsIDOMRange *)(blockSections->ElementAt(0));
|
|
|
|
while (subRange)
|
1999-05-01 02:40:18 +04:00
|
|
|
{
|
1999-08-19 17:30:48 +04:00
|
|
|
nsCOMPtr<nsIDOMNode>startParent;
|
|
|
|
res = subRange->GetStartParent(getter_AddRefs(startParent));
|
|
|
|
if (NS_SUCCEEDED(res) && startParent)
|
1999-05-01 02:40:18 +04:00
|
|
|
{
|
1999-08-19 17:30:48 +04:00
|
|
|
nsCOMPtr<nsIDOMElement> blockParent;
|
1999-09-22 09:52:44 +04:00
|
|
|
if (aGetLists)
|
|
|
|
{
|
|
|
|
// Get the "ol", "ul", or "dl" parent element
|
|
|
|
res = GetElementOrParentByTagName("list", startParent, getter_AddRefs(blockParent));
|
2000-03-29 16:53:23 +04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
blockParent = do_QueryInterface(GetBlockNodeParent(startParent));
|
1999-09-22 09:52:44 +04:00
|
|
|
}
|
1999-08-19 17:30:48 +04:00
|
|
|
if (NS_SUCCEEDED(res) && blockParent)
|
1999-05-01 02:40:18 +04:00
|
|
|
{
|
1999-08-19 17:30:48 +04:00
|
|
|
nsAutoString blockParentTag;
|
|
|
|
blockParent->GetTagName(blockParentTag);
|
|
|
|
PRBool isRoot;
|
|
|
|
IsRootTag(blockParentTag, isRoot);
|
2000-03-24 03:26:47 +03:00
|
|
|
if ((!isRoot) && (-1==aTagList->IndexOf(blockParentTag))) {
|
1999-08-19 17:30:48 +04:00
|
|
|
aTagList->AppendString(blockParentTag);
|
1999-05-01 02:40:18 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1999-08-19 17:30:48 +04:00
|
|
|
NS_RELEASE(subRange);
|
|
|
|
if (NS_FAILED(res))
|
|
|
|
break; // don't return here, need to release blockSections
|
|
|
|
blockSections->RemoveElementAt(0);
|
|
|
|
subRange = (nsIDOMRange *)(blockSections->ElementAt(0));
|
1999-05-01 02:40:18 +04:00
|
|
|
}
|
|
|
|
}
|
1999-08-19 17:30:48 +04:00
|
|
|
NS_RELEASE(blockSections);
|
1999-05-01 02:40:18 +04:00
|
|
|
}
|
1999-06-03 10:00:23 +04:00
|
|
|
return res;
|
1999-05-01 02:40:18 +04:00
|
|
|
}
|
|
|
|
|
1999-09-22 09:52:44 +04:00
|
|
|
|
|
|
|
// get the paragraph style(s) for the selection
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::GetParagraphTags(nsStringArray *aTagList)
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
if (gNoisy) { printf("---------- nsHTMLEditor::GetPargraphTags ----------\n"); }
|
|
|
|
#endif
|
|
|
|
return GetParentBlockTags(aTagList, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::GetListTags(nsStringArray *aTagList)
|
|
|
|
{
|
|
|
|
return GetParentBlockTags(aTagList, PR_TRUE);
|
|
|
|
}
|
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::MakeOrChangeList(const nsString& aListType)
|
1999-05-01 02:40:18 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
nsresult res;
|
|
|
|
if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
|
|
|
PRBool cancel, handled;
|
|
|
|
|
|
|
|
nsAutoEditBatch beginBatching(this);
|
|
|
|
nsAutoRules beginRulesSniffing(this, kOpMakeList, nsIEditor::eNext);
|
1999-04-20 21:47:12 +04:00
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
// pre-process
|
1999-08-19 17:30:48 +04:00
|
|
|
res = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!selection) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsTextRulesInfo ruleInfo(nsHTMLEditRules::kMakeList);
|
2000-03-26 15:39:08 +04:00
|
|
|
if (aListType.Equals("ol")) ruleInfo.bOrdered = PR_TRUE;
|
1999-08-09 05:37:50 +04:00
|
|
|
else ruleInfo.bOrdered = PR_FALSE;
|
1999-10-06 23:34:09 +04:00
|
|
|
res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
|
1999-08-09 05:37:50 +04:00
|
|
|
if (cancel || (NS_FAILED(res))) return res;
|
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
if (!handled)
|
|
|
|
{
|
|
|
|
// Find out if the selection is collapsed:
|
|
|
|
PRBool isCollapsed;
|
|
|
|
res = selection->GetIsCollapsed(&isCollapsed);
|
|
|
|
if (NS_FAILED(res)) return res;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> node;
|
|
|
|
PRInt32 offset;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
res = GetStartNodeAndOffset(selection, &node, &offset);
|
|
|
|
if (!node) res = NS_ERROR_FAILURE;
|
|
|
|
if (NS_FAILED(res)) return res;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
if (isCollapsed)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
1999-10-06 23:34:09 +04:00
|
|
|
// have to find a place to put the list
|
|
|
|
nsCOMPtr<nsIDOMNode> parent = node;
|
|
|
|
nsCOMPtr<nsIDOMNode> topChild = node;
|
|
|
|
nsCOMPtr<nsIDOMNode> tmp;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
while ( !CanContainTag(parent, aListType))
|
|
|
|
{
|
|
|
|
parent->GetParentNode(getter_AddRefs(tmp));
|
|
|
|
if (!tmp) return NS_ERROR_FAILURE;
|
|
|
|
topChild = parent;
|
|
|
|
parent = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parent != node)
|
|
|
|
{
|
|
|
|
// we need to split up to the child of parent
|
|
|
|
res = SplitNodeDeep(topChild, node, offset, &offset);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
// make a list
|
|
|
|
nsCOMPtr<nsIDOMNode> newList;
|
|
|
|
res = CreateNode(aListType, parent, offset, getter_AddRefs(newList));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
// make a list item
|
|
|
|
nsAutoString tag("li");
|
|
|
|
nsCOMPtr<nsIDOMNode> newItem;
|
|
|
|
res = CreateNode(tag, newList, 0, getter_AddRefs(newItem));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
res = selection->Collapse(newItem,0);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
1999-10-06 23:34:09 +04:00
|
|
|
|
|
|
|
res = mRules->DidDoAction(selection, &ruleInfo, res);
|
1999-08-09 05:37:50 +04:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
1999-08-10 01:51:19 +04:00
|
|
|
|
1999-09-06 23:47:25 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::RemoveList(const nsString& aListType)
|
|
|
|
{
|
|
|
|
nsresult res;
|
|
|
|
if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
1999-10-06 23:34:09 +04:00
|
|
|
PRBool cancel, handled;
|
1999-09-06 23:47:25 +04:00
|
|
|
|
|
|
|
nsAutoEditBatch beginBatching(this);
|
1999-12-07 11:30:19 +03:00
|
|
|
nsAutoRules beginRulesSniffing(this, kOpRemoveList, nsIEditor::eNext);
|
1999-09-06 23:47:25 +04:00
|
|
|
|
|
|
|
// pre-process
|
|
|
|
res = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!selection) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
nsTextRulesInfo ruleInfo(nsHTMLEditRules::kRemoveList);
|
2000-03-26 15:39:08 +04:00
|
|
|
if (aListType.Equals("ol")) ruleInfo.bOrdered = PR_TRUE;
|
1999-09-06 23:47:25 +04:00
|
|
|
else ruleInfo.bOrdered = PR_FALSE;
|
1999-10-06 23:34:09 +04:00
|
|
|
res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
|
1999-09-06 23:47:25 +04:00
|
|
|
if (cancel || (NS_FAILED(res))) return res;
|
|
|
|
|
|
|
|
// no default behavior for this yet. what would it mean?
|
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
res = mRules->DidDoAction(selection, &ruleInfo, res);
|
1999-09-06 23:47:25 +04:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-08-10 01:51:19 +04:00
|
|
|
NS_IMETHODIMP
|
1999-08-18 12:13:06 +04:00
|
|
|
nsHTMLEditor::InsertBasicBlock(const nsString& aBlockType)
|
1999-08-10 01:51:19 +04:00
|
|
|
{
|
|
|
|
nsresult res;
|
|
|
|
if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
1999-10-06 23:34:09 +04:00
|
|
|
PRBool cancel, handled;
|
1999-08-10 01:51:19 +04:00
|
|
|
|
|
|
|
nsAutoEditBatch beginBatching(this);
|
1999-12-07 11:30:19 +03:00
|
|
|
nsAutoRules beginRulesSniffing(this, kOpMakeBasicBlock, nsIEditor::eNext);
|
1999-08-10 01:51:19 +04:00
|
|
|
|
|
|
|
// pre-process
|
1999-08-19 17:30:48 +04:00
|
|
|
res = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!selection) return NS_ERROR_NULL_POINTER;
|
1999-08-18 12:13:06 +04:00
|
|
|
nsTextRulesInfo ruleInfo(nsHTMLEditRules::kMakeBasicBlock);
|
|
|
|
ruleInfo.blockType = &aBlockType;
|
1999-10-06 23:34:09 +04:00
|
|
|
res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
|
1999-08-10 01:51:19 +04:00
|
|
|
if (cancel || (NS_FAILED(res))) return res;
|
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
if (!handled)
|
|
|
|
{
|
|
|
|
// Find out if the selection is collapsed:
|
|
|
|
PRBool isCollapsed;
|
|
|
|
res = selection->GetIsCollapsed(&isCollapsed);
|
|
|
|
if (NS_FAILED(res)) return res;
|
1999-08-10 01:51:19 +04:00
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> node;
|
|
|
|
PRInt32 offset;
|
1999-08-10 01:51:19 +04:00
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
res = GetStartNodeAndOffset(selection, &node, &offset);
|
|
|
|
if (!node) res = NS_ERROR_FAILURE;
|
|
|
|
if (NS_FAILED(res)) return res;
|
1999-08-10 01:51:19 +04:00
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
if (isCollapsed)
|
1999-08-10 01:51:19 +04:00
|
|
|
{
|
1999-10-06 23:34:09 +04:00
|
|
|
// have to find a place to put the block
|
|
|
|
nsCOMPtr<nsIDOMNode> parent = node;
|
|
|
|
nsCOMPtr<nsIDOMNode> topChild = node;
|
|
|
|
nsCOMPtr<nsIDOMNode> tmp;
|
1999-08-10 01:51:19 +04:00
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
while ( !CanContainTag(parent, aBlockType))
|
|
|
|
{
|
|
|
|
parent->GetParentNode(getter_AddRefs(tmp));
|
|
|
|
if (!tmp) return NS_ERROR_FAILURE;
|
|
|
|
topChild = parent;
|
|
|
|
parent = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parent != node)
|
|
|
|
{
|
|
|
|
// we need to split up to the child of parent
|
|
|
|
res = SplitNodeDeep(topChild, node, offset, &offset);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
}
|
1999-08-10 01:51:19 +04:00
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
// make a block
|
|
|
|
nsCOMPtr<nsIDOMNode> newBlock;
|
|
|
|
res = CreateNode(aBlockType, parent, offset, getter_AddRefs(newBlock));
|
|
|
|
if (NS_FAILED(res)) return res;
|
1999-08-18 12:13:06 +04:00
|
|
|
|
2000-02-25 07:39:30 +03:00
|
|
|
// reposition selection to inside the block
|
1999-10-06 23:34:09 +04:00
|
|
|
res = selection->Collapse(newBlock,0);
|
1999-11-25 03:19:45 +03:00
|
|
|
if (NS_FAILED(res)) return res;
|
1999-10-06 23:34:09 +04:00
|
|
|
}
|
1999-08-10 01:51:19 +04:00
|
|
|
}
|
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
res = mRules->DidDoAction(selection, &ruleInfo, res);
|
1999-08-10 01:51:19 +04:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::Indent(const nsString& aIndent)
|
|
|
|
{
|
|
|
|
nsresult res;
|
|
|
|
if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
|
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
PRBool cancel, handled;
|
1999-12-07 11:30:19 +03:00
|
|
|
PRInt32 theAction = nsHTMLEditRules::kIndent;
|
|
|
|
PRInt32 opID = kOpIndent;
|
2000-03-26 15:39:08 +04:00
|
|
|
if (aIndent.Equals("outdent"))
|
1999-12-07 11:30:19 +03:00
|
|
|
{
|
|
|
|
theAction = nsHTMLEditRules::kOutdent;
|
|
|
|
opID = kOpOutdent;
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
nsAutoEditBatch beginBatching(this);
|
1999-12-07 11:30:19 +03:00
|
|
|
nsAutoRules beginRulesSniffing(this, opID, nsIEditor::eNext);
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
// pre-process
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
1999-08-19 17:30:48 +04:00
|
|
|
res = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!selection) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
1999-12-07 11:30:19 +03:00
|
|
|
nsTextRulesInfo ruleInfo(theAction);
|
1999-10-06 23:34:09 +04:00
|
|
|
res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
|
1999-08-09 05:37:50 +04:00
|
|
|
if (cancel || (NS_FAILED(res))) return res;
|
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
if (!handled)
|
|
|
|
{
|
|
|
|
// Do default - insert a blockquote node if selection collapsed
|
|
|
|
nsCOMPtr<nsIDOMNode> node;
|
|
|
|
PRInt32 offset;
|
|
|
|
PRBool isCollapsed;
|
|
|
|
res = selection->GetIsCollapsed(&isCollapsed);
|
|
|
|
if (NS_FAILED(res)) return res;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
res = GetStartNodeAndOffset(selection, &node, &offset);
|
|
|
|
if (!node) res = NS_ERROR_FAILURE;
|
|
|
|
if (NS_FAILED(res)) return res;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
nsAutoString inward("indent");
|
|
|
|
if (aIndent == inward)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
1999-10-06 23:34:09 +04:00
|
|
|
if (isCollapsed)
|
1999-04-20 21:47:12 +04:00
|
|
|
{
|
1999-10-06 23:34:09 +04:00
|
|
|
// have to find a place to put the blockquote
|
|
|
|
nsCOMPtr<nsIDOMNode> parent = node;
|
|
|
|
nsCOMPtr<nsIDOMNode> topChild = node;
|
|
|
|
nsCOMPtr<nsIDOMNode> tmp;
|
|
|
|
nsAutoString bq("blockquote");
|
|
|
|
while ( !CanContainTag(parent, bq))
|
|
|
|
{
|
|
|
|
parent->GetParentNode(getter_AddRefs(tmp));
|
|
|
|
if (!tmp) return NS_ERROR_FAILURE;
|
|
|
|
topChild = parent;
|
|
|
|
parent = tmp;
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-10-06 23:34:09 +04:00
|
|
|
if (parent != node)
|
|
|
|
{
|
|
|
|
// we need to split up to the child of parent
|
|
|
|
res = SplitNodeDeep(topChild, node, offset, &offset);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
// make a blockquote
|
|
|
|
nsCOMPtr<nsIDOMNode> newBQ;
|
|
|
|
res = CreateNode(bq, parent, offset, getter_AddRefs(newBQ));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
// put a space in it so layout will draw the list item
|
|
|
|
res = selection->Collapse(newBQ,0);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
nsAutoString theText(" ");
|
|
|
|
res = InsertText(theText);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
// reposition selection to before the space character
|
|
|
|
res = GetStartNodeAndOffset(selection, &node, &offset);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
res = selection->Collapse(node,0);
|
1999-08-09 05:37:50 +04:00
|
|
|
if (NS_FAILED(res)) return res;
|
1999-04-20 21:47:12 +04:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
}
|
1999-10-06 23:34:09 +04:00
|
|
|
res = mRules->DidDoAction(selection, &ruleInfo, res);
|
1999-08-09 05:37:50 +04:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
//TODO: IMPLEMENT ALIGNMENT!
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::Align(const nsString& aAlignType)
|
|
|
|
{
|
|
|
|
nsAutoEditBatch beginBatching(this);
|
1999-12-07 11:30:19 +03:00
|
|
|
nsAutoRules beginRulesSniffing(this, kOpAlign, nsIEditor::eNext);
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> node;
|
1999-10-06 23:34:09 +04:00
|
|
|
PRBool cancel, handled;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
// Find out if the selection is collapsed:
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
|
|
|
nsresult res = GetSelection(getter_AddRefs(selection));
|
1999-08-19 17:30:48 +04:00
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!selection) return NS_ERROR_NULL_POINTER;
|
1999-08-09 05:37:50 +04:00
|
|
|
nsTextRulesInfo ruleInfo(nsHTMLEditRules::kAlign);
|
|
|
|
ruleInfo.alignType = &aAlignType;
|
1999-10-06 23:34:09 +04:00
|
|
|
res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
|
|
|
|
if (cancel || NS_FAILED(res))
|
|
|
|
return res;
|
|
|
|
|
|
|
|
res = mRules->DidDoAction(selection, &ruleInfo, res);
|
1999-08-09 05:37:50 +04:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::GetElementOrParentByTagName(const nsString &aTagName, nsIDOMNode *aNode, nsIDOMElement** aReturn)
|
|
|
|
{
|
|
|
|
if (aTagName.Length() == 0 || !aReturn )
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
nsresult res = NS_OK;
|
2000-03-17 04:46:17 +03:00
|
|
|
nsCOMPtr<nsIDOMNode> currentNode;
|
|
|
|
|
|
|
|
if (aNode)
|
|
|
|
currentNode = aNode;
|
|
|
|
else
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
2000-03-17 04:46:17 +03:00
|
|
|
// If no node supplied, get it from anchor node of current selection
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMSelection>selection;
|
1999-08-19 17:30:48 +04:00
|
|
|
res = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!selection) return NS_ERROR_NULL_POINTER;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
2000-03-17 04:46:17 +03:00
|
|
|
nsCOMPtr<nsIDOMNode> anchorNode;
|
|
|
|
res = selection->GetAnchorNode(getter_AddRefs(anchorNode));
|
|
|
|
if(NS_FAILED(res)) return res;
|
|
|
|
if (!anchorNode) return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
// Try to get the actual selected node
|
|
|
|
PRBool hasChildren = PR_FALSE;
|
|
|
|
anchorNode->HasChildNodes(&hasChildren);
|
|
|
|
if (hasChildren)
|
|
|
|
{
|
|
|
|
PRInt32 offset;
|
|
|
|
res = selection->GetAnchorOffset(&offset);
|
|
|
|
if(NS_FAILED(res)) return res;
|
|
|
|
currentNode = nsEditor::GetChildAt(anchorNode, offset);
|
|
|
|
}
|
2000-03-21 09:05:24 +03:00
|
|
|
// anchor node is probably a text node - just use that
|
|
|
|
if (!currentNode)
|
|
|
|
currentNode = anchorNode;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
nsAutoString TagName = aTagName;
|
|
|
|
TagName.ToLowerCase();
|
|
|
|
PRBool getLink = IsLink(TagName);
|
|
|
|
PRBool getNamedAnchor = IsNamedAnchor(TagName);
|
|
|
|
if ( getLink || getNamedAnchor)
|
|
|
|
{
|
|
|
|
TagName = "a";
|
|
|
|
}
|
|
|
|
PRBool findTableCell = aTagName.EqualsIgnoreCase("td");
|
1999-09-22 09:52:44 +04:00
|
|
|
PRBool findList = aTagName.EqualsIgnoreCase("list");
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
// default is null - no element found
|
|
|
|
*aReturn = nsnull;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNode> parent;
|
|
|
|
PRBool bNodeFound = PR_FALSE;
|
|
|
|
|
|
|
|
while (PR_TRUE)
|
|
|
|
{
|
1999-09-22 09:52:44 +04:00
|
|
|
nsAutoString currentTagName;
|
1999-08-09 05:37:50 +04:00
|
|
|
// Test if we have a link (an anchor with href set)
|
|
|
|
if ( (getLink && IsLinkNode(currentNode)) ||
|
|
|
|
(getNamedAnchor && IsNamedAnchorNode(currentNode)) )
|
|
|
|
{
|
|
|
|
bNodeFound = PR_TRUE;
|
|
|
|
break;
|
|
|
|
} else {
|
1999-09-22 09:52:44 +04:00
|
|
|
if (findList)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
1999-09-22 09:52:44 +04:00
|
|
|
// Match "ol", "ul", or "dl" for lists
|
|
|
|
if (IsListNode(currentNode))
|
|
|
|
goto NODE_FOUND;
|
|
|
|
|
|
|
|
} else if (findTableCell)
|
|
|
|
{
|
|
|
|
// Table cells are another special case:
|
|
|
|
// Match either "td" or "th" for them
|
|
|
|
if (IsCellNode(currentNode))
|
|
|
|
goto NODE_FOUND;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
currentNode->GetNodeName(currentTagName);
|
|
|
|
if (currentTagName.EqualsIgnoreCase(TagName))
|
|
|
|
{
|
|
|
|
NODE_FOUND:
|
|
|
|
bNodeFound = PR_TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
// Search up the parent chain
|
|
|
|
// We should never fail because of root test below, but lets be safe
|
1999-09-30 00:08:15 +04:00
|
|
|
// XXX: ERROR_HANDLING error return code lost
|
1999-08-09 05:37:50 +04:00
|
|
|
if (!NS_SUCCEEDED(currentNode->GetParentNode(getter_AddRefs(parent))) || !parent)
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Stop searching if parent is a body tag
|
|
|
|
nsAutoString parentTagName;
|
|
|
|
parent->GetNodeName(parentTagName);
|
|
|
|
// Note: Originally used IsRoot to stop at table cells,
|
|
|
|
// but that's too messy when you are trying to find the parent table
|
|
|
|
//PRBool isRoot;
|
|
|
|
//if (!NS_SUCCEEDED(IsRootTag(parentTagName, isRoot)) || isRoot)
|
|
|
|
if(parentTagName.EqualsIgnoreCase("body"))
|
|
|
|
break;
|
|
|
|
|
|
|
|
currentNode = parent;
|
|
|
|
}
|
|
|
|
if (bNodeFound)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMElement> currentElement = do_QueryInterface(currentNode);
|
|
|
|
if (currentElement)
|
|
|
|
{
|
|
|
|
*aReturn = currentElement;
|
|
|
|
// Getters must addref
|
|
|
|
NS_ADDREF(*aReturn);
|
|
|
|
}
|
|
|
|
}
|
2000-02-10 08:14:52 +03:00
|
|
|
else res = NS_EDITOR_ELEMENT_NOT_FOUND;
|
|
|
|
|
|
|
|
return res;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::GetSelectedElement(const nsString& aTagName, nsIDOMElement** aReturn)
|
|
|
|
{
|
|
|
|
if (!aReturn )
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
// default is null - no element found
|
|
|
|
*aReturn = nsnull;
|
|
|
|
|
|
|
|
nsAutoString TagName = aTagName;
|
|
|
|
TagName.ToLowerCase();
|
|
|
|
// Empty string indicates we should match any element tag
|
2000-03-26 15:39:08 +04:00
|
|
|
PRBool anyTag = (TagName.IsEmpty());
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
//Note that this doesn't need to go through the transaction system
|
|
|
|
|
|
|
|
nsresult res=NS_ERROR_NOT_INITIALIZED;
|
|
|
|
//PRBool first=PR_TRUE;
|
|
|
|
nsCOMPtr<nsIDOMSelection>selection;
|
1999-08-19 17:30:48 +04:00
|
|
|
res = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!selection) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
PRBool isCollapsed;
|
|
|
|
selection->GetIsCollapsed(&isCollapsed);
|
|
|
|
nsCOMPtr<nsIDOMElement> selectedElement;
|
|
|
|
PRBool bNodeFound = PR_FALSE;
|
|
|
|
|
|
|
|
if (IsLink(TagName))
|
|
|
|
{
|
|
|
|
// Link tag is a special case - we return the anchor node
|
|
|
|
// found for any selection that is totally within a link,
|
|
|
|
// included a collapsed selection (just a caret in a link)
|
|
|
|
nsCOMPtr<nsIDOMNode> anchorNode;
|
|
|
|
res = selection->GetAnchorNode(getter_AddRefs(anchorNode));
|
1999-09-30 00:08:15 +04:00
|
|
|
if (NS_FAILED(res)) return res;
|
1999-08-09 05:37:50 +04:00
|
|
|
PRInt32 anchorOffset = -1;
|
|
|
|
if (anchorNode)
|
|
|
|
selection->GetAnchorOffset(&anchorOffset);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNode> focusNode;
|
1999-08-19 17:30:48 +04:00
|
|
|
res = selection->GetFocusNode(getter_AddRefs(focusNode));
|
1999-09-30 00:08:15 +04:00
|
|
|
if (NS_FAILED(res)) return res;
|
1999-08-09 05:37:50 +04:00
|
|
|
PRInt32 focusOffset = -1;
|
|
|
|
if (focusNode)
|
|
|
|
selection->GetFocusOffset(&focusOffset);
|
|
|
|
|
|
|
|
// Link node must be the same for both ends of selection
|
|
|
|
if (NS_SUCCEEDED(res) && anchorNode)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG_cmanske
|
|
|
|
{
|
|
|
|
nsAutoString name;
|
|
|
|
anchorNode->GetNodeName(name);
|
|
|
|
printf("GetSelectedElement: Anchor node of selection: ");
|
|
|
|
wprintf(name.GetUnicode());
|
|
|
|
printf(" Offset: %d\n", anchorOffset);
|
|
|
|
focusNode->GetNodeName(name);
|
|
|
|
printf("Focus node of selection: ");
|
|
|
|
wprintf(name.GetUnicode());
|
|
|
|
printf(" Offset: %d\n", focusOffset);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
nsCOMPtr<nsIDOMElement> parentLinkOfAnchor;
|
|
|
|
res = GetElementOrParentByTagName("href", anchorNode, getter_AddRefs(parentLinkOfAnchor));
|
1999-09-30 00:08:15 +04:00
|
|
|
// XXX: ERROR_HANDLING can parentLinkOfAnchor be null?
|
1999-08-09 05:37:50 +04:00
|
|
|
if (NS_SUCCEEDED(res) && parentLinkOfAnchor)
|
|
|
|
{
|
|
|
|
if (isCollapsed)
|
|
|
|
{
|
|
|
|
// We have just a caret in the link
|
|
|
|
bNodeFound = PR_TRUE;
|
|
|
|
} else if(focusNode)
|
|
|
|
{ // Link node must be the same for both ends of selection
|
|
|
|
nsCOMPtr<nsIDOMElement> parentLinkOfFocus;
|
|
|
|
res = GetElementOrParentByTagName("href", focusNode, getter_AddRefs(parentLinkOfFocus));
|
|
|
|
if (NS_SUCCEEDED(res) && parentLinkOfFocus == parentLinkOfAnchor)
|
|
|
|
bNodeFound = PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We found a link node parent
|
|
|
|
if (bNodeFound) {
|
|
|
|
// GetElementOrParentByTagName addref'd this, so we don't need to do it here
|
|
|
|
*aReturn = parentLinkOfAnchor;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (anchorOffset >= 0) // Check if link node is the only thing selected
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> anchorChild;
|
|
|
|
anchorChild = GetChildAt(anchorNode,anchorOffset);
|
|
|
|
if (anchorChild && IsLinkNode(anchorChild) &&
|
|
|
|
(anchorNode == focusNode) && focusOffset == (anchorOffset+1))
|
|
|
|
{
|
|
|
|
selectedElement = do_QueryInterface(anchorChild);
|
|
|
|
bNodeFound = PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!bNodeFound && !isCollapsed) // Don't bother to examine selection if it is collapsed
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIEnumerator> enumerator;
|
|
|
|
res = selection->GetEnumerator(getter_AddRefs(enumerator));
|
1999-09-30 01:45:50 +04:00
|
|
|
if (NS_SUCCEEDED(res))
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
1999-09-30 01:45:50 +04:00
|
|
|
if(!enumerator)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
enumerator->First();
|
|
|
|
nsCOMPtr<nsISupports> currentItem;
|
|
|
|
res = enumerator->CurrentItem(getter_AddRefs(currentItem));
|
|
|
|
if ((NS_SUCCEEDED(res)) && currentItem)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
|
|
|
|
nsCOMPtr<nsIContentIterator> iter;
|
|
|
|
res = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull,
|
2000-02-01 17:26:27 +03:00
|
|
|
NS_GET_IID(nsIContentIterator),
|
1999-08-09 05:37:50 +04:00
|
|
|
getter_AddRefs(iter));
|
1999-09-30 00:08:15 +04:00
|
|
|
// XXX: ERROR_HANDLING XPCOM usage
|
1999-08-09 05:37:50 +04:00
|
|
|
if ((NS_SUCCEEDED(res)) && iter)
|
|
|
|
{
|
|
|
|
iter->Init(range);
|
|
|
|
// loop through the content iterator for each content node
|
|
|
|
nsCOMPtr<nsIContent> content;
|
1999-10-28 07:16:48 +04:00
|
|
|
while (NS_ENUMERATOR_FALSE == iter->IsDone())
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
|
|
|
res = iter->CurrentNode(getter_AddRefs(content));
|
|
|
|
// Note likely!
|
|
|
|
if (NS_FAILED(res))
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
// Query interface to cast nsIContent to nsIDOMNode
|
|
|
|
// then get tagType to compare to aTagName
|
|
|
|
// Clone node of each desired type and append it to the aDomFrag
|
|
|
|
selectedElement = do_QueryInterface(content);
|
|
|
|
if (selectedElement)
|
|
|
|
{
|
|
|
|
// If we already found a node, then we have another element,
|
2000-02-15 18:54:05 +03:00
|
|
|
// thus there's not just one element selected
|
1999-08-09 05:37:50 +04:00
|
|
|
if (bNodeFound)
|
|
|
|
{
|
2000-02-15 18:54:05 +03:00
|
|
|
bNodeFound = PR_FALSE;
|
1999-08-09 05:37:50 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsAutoString domTagName;
|
|
|
|
selectedElement->GetNodeName(domTagName);
|
|
|
|
domTagName.ToLowerCase();
|
|
|
|
|
|
|
|
if (anyTag)
|
|
|
|
{
|
|
|
|
// Get name of first selected element
|
|
|
|
selectedElement->GetTagName(TagName);
|
|
|
|
TagName.ToLowerCase();
|
|
|
|
anyTag = PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The "A" tag is a pain,
|
|
|
|
// used for both link(href is set) and "Named Anchor"
|
|
|
|
nsCOMPtr<nsIDOMNode> selectedNode = do_QueryInterface(selectedElement);
|
|
|
|
if ( (IsLink(TagName) && IsLinkNode(selectedNode)) ||
|
|
|
|
(IsNamedAnchor(TagName) && IsNamedAnchorNode(selectedNode)) )
|
|
|
|
{
|
|
|
|
bNodeFound = PR_TRUE;
|
|
|
|
} else if (TagName == domTagName) { // All other tag names are handled here
|
|
|
|
bNodeFound = PR_TRUE;
|
|
|
|
}
|
|
|
|
if (!bNodeFound)
|
|
|
|
{
|
|
|
|
// Check if node we have is really part of the selection???
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
iter->Next();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Should never get here?
|
|
|
|
isCollapsed = PR_TRUE;
|
|
|
|
printf("isCollapsed was FALSE, but no elements found in selection\n");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
printf("Could not create enumerator for GetSelectionProperties\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (bNodeFound)
|
|
|
|
{
|
|
|
|
|
1999-09-01 05:22:37 +04:00
|
|
|
*aReturn = selectedElement;
|
|
|
|
if (selectedElement)
|
|
|
|
{
|
|
|
|
// Getters must addref
|
|
|
|
NS_ADDREF(*aReturn);
|
|
|
|
}
|
2000-02-10 08:14:52 +03:00
|
|
|
}
|
|
|
|
else res = NS_EDITOR_ELEMENT_NOT_FOUND;
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::CreateElementWithDefaults(const nsString& aTagName, nsIDOMElement** aReturn)
|
|
|
|
{
|
|
|
|
nsresult res=NS_ERROR_NOT_INITIALIZED;
|
|
|
|
if (aReturn)
|
|
|
|
*aReturn = nsnull;
|
|
|
|
|
2000-03-26 15:39:08 +04:00
|
|
|
if (aTagName.IsEmpty() || !aReturn)
|
1999-08-09 05:37:50 +04:00
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
nsAutoString TagName = aTagName;
|
|
|
|
TagName.ToLowerCase();
|
|
|
|
nsAutoString realTagName;
|
|
|
|
|
|
|
|
if (IsLink(TagName) || IsNamedAnchor(TagName))
|
|
|
|
{
|
|
|
|
realTagName = "a";
|
|
|
|
} else {
|
|
|
|
realTagName = TagName;
|
|
|
|
}
|
|
|
|
//We don't use editor's CreateElement because we don't want to
|
|
|
|
// go through the transaction system
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMElement>newElement;
|
1999-08-25 14:51:55 +04:00
|
|
|
nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(mDocWeak);
|
|
|
|
if (!doc) return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
res = doc->CreateElement(realTagName, getter_AddRefs(newElement));
|
1999-08-09 05:37:50 +04:00
|
|
|
if (NS_FAILED(res) || !newElement)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2000-01-28 04:17:23 +03:00
|
|
|
// Mark the new element dirty, so it will be formatted
|
|
|
|
newElement->SetAttribute("_moz_dirty", "");
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
// Set default values for new elements
|
|
|
|
if (TagName.Equals("hr"))
|
|
|
|
{
|
1999-08-27 08:12:47 +04:00
|
|
|
// Note that we read the user's attributes for these from prefs (in InsertHLine JS)
|
|
|
|
newElement->SetAttribute("align","center");
|
|
|
|
newElement->SetAttribute("width","100%");
|
2000-01-14 02:33:00 +03:00
|
|
|
newElement->SetAttribute("size","2");
|
1999-08-09 05:37:50 +04:00
|
|
|
} else if (TagName.Equals("table"))
|
|
|
|
{
|
|
|
|
newElement->SetAttribute("cellpadding","2");
|
|
|
|
newElement->SetAttribute("cellspacing","2");
|
|
|
|
newElement->SetAttribute("border","1");
|
|
|
|
} else if (TagName.Equals("tr"))
|
|
|
|
{
|
|
|
|
newElement->SetAttribute("valign","top");
|
|
|
|
} else if (TagName.Equals("td"))
|
|
|
|
{
|
|
|
|
newElement->SetAttribute("valign","top");
|
|
|
|
|
1999-11-25 03:19:45 +03:00
|
|
|
// I'm def'ing this out to see if auto br insertion is working here
|
|
|
|
#if 0
|
1999-08-09 05:37:50 +04:00
|
|
|
// Insert the default space in a cell so border displays
|
|
|
|
nsCOMPtr<nsIDOMNode> newCellNode = do_QueryInterface(newElement);
|
|
|
|
if (newCellNode)
|
|
|
|
{
|
|
|
|
// TODO: This should probably be in the RULES code or
|
|
|
|
// preference based for "should we add the nbsp"
|
|
|
|
nsCOMPtr<nsIDOMText>newTextNode;
|
1999-09-15 03:40:16 +04:00
|
|
|
nsAutoString space;
|
1999-08-09 05:37:50 +04:00
|
|
|
// Set contents to the   character by concatanating the char code
|
|
|
|
space += nbsp;
|
|
|
|
// If we fail here, we return NS_OK anyway, since we have an OK cell node
|
1999-08-25 14:51:55 +04:00
|
|
|
nsresult result = doc->CreateTextNode(space, getter_AddRefs(newTextNode));
|
1999-09-30 00:08:15 +04:00
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
if (!newTextNode) return NS_ERROR_NULL_POINTER;
|
1999-08-19 17:30:48 +04:00
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNode>resultNode;
|
|
|
|
result = newCellNode->AppendChild(newTextNode, getter_AddRefs(resultNode));
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
1999-11-25 03:19:45 +03:00
|
|
|
#endif
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
1999-12-02 01:20:50 +03:00
|
|
|
// ADD OTHER TAGS HERE
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
if (NS_SUCCEEDED(res))
|
|
|
|
{
|
|
|
|
*aReturn = newElement;
|
1999-10-14 04:13:27 +04:00
|
|
|
// Getters must addref
|
|
|
|
NS_ADDREF(*aReturn);
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::InsertLinkAroundSelection(nsIDOMElement* aAnchorElement)
|
|
|
|
{
|
|
|
|
nsresult res=NS_ERROR_NULL_POINTER;
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
|
|
|
|
2000-02-15 18:54:05 +03:00
|
|
|
if (!aAnchorElement) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
// We must have a real selection
|
|
|
|
res = GetSelection(getter_AddRefs(selection));
|
1999-08-19 17:30:48 +04:00
|
|
|
if (!selection)
|
|
|
|
{
|
|
|
|
res = NS_ERROR_NULL_POINTER;
|
|
|
|
}
|
2000-02-15 18:54:05 +03:00
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!selection) return NS_ERROR_NULL_POINTER;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
PRBool isCollapsed;
|
|
|
|
res = selection->GetIsCollapsed(&isCollapsed);
|
|
|
|
if (NS_FAILED(res))
|
|
|
|
isCollapsed = PR_TRUE;
|
|
|
|
|
|
|
|
if (isCollapsed)
|
|
|
|
{
|
|
|
|
printf("InsertLinkAroundSelection called but there is no selection!!!\n");
|
|
|
|
res = NS_OK;
|
|
|
|
} else {
|
|
|
|
// Be sure we were given an anchor element
|
|
|
|
nsCOMPtr<nsIDOMHTMLAnchorElement> anchor = do_QueryInterface(aAnchorElement);
|
|
|
|
if (anchor)
|
|
|
|
{
|
|
|
|
nsAutoString href;
|
2000-02-15 18:54:05 +03:00
|
|
|
res = anchor->GetHref(href);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (href.GetUnicode() && href.Length() > 0)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
|
|
|
nsAutoEditBatch beginBatching(this);
|
|
|
|
const nsString attribute("href");
|
|
|
|
SetInlineProperty(nsIEditProperty::a, &attribute, &href);
|
|
|
|
//TODO: Enumerate through other properties of the anchor tag
|
|
|
|
// and set those as well.
|
|
|
|
// Optimization: Modify SetTextProperty to set all attributes at once?
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2000-02-15 18:54:05 +03:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::SetBackgroundColor(const nsString& aColor)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
1999-08-25 14:51:55 +04:00
|
|
|
NS_PRECONDITION(mDocWeak, "Missing Editor DOM Document");
|
1999-08-09 05:37:50 +04:00
|
|
|
|
2000-01-14 02:33:00 +03:00
|
|
|
// Find a selected or enclosing table element to set background on
|
|
|
|
nsCOMPtr<nsIDOMElement> element;
|
2000-03-21 09:05:24 +03:00
|
|
|
PRInt32 selectedCount;
|
2000-01-14 02:33:00 +03:00
|
|
|
nsAutoString tagName;
|
2000-03-21 09:05:24 +03:00
|
|
|
nsresult res = GetSelectedOrParentTableElement(*getter_AddRefs(element), tagName, selectedCount);
|
2000-01-14 02:33:00 +03:00
|
|
|
if (NS_FAILED(res)) return res;
|
2000-03-21 09:05:24 +03:00
|
|
|
if (element)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
2000-03-21 09:05:24 +03:00
|
|
|
if (selectedCount > 0)
|
|
|
|
{
|
|
|
|
// Traverse all selected cells
|
|
|
|
nsCOMPtr<nsIDOMElement> cell;
|
2000-03-31 08:18:29 +04:00
|
|
|
res = GetFirstSelectedCell(getter_AddRefs(cell), nsnull);
|
2000-03-21 09:05:24 +03:00
|
|
|
if (NS_SUCCEEDED(res) && cell)
|
|
|
|
{
|
|
|
|
while(cell)
|
|
|
|
{
|
|
|
|
SetAttribute(cell, "bgcolor", aColor);
|
2000-03-31 08:18:29 +04:00
|
|
|
GetNextSelectedCell(getter_AddRefs(cell), nsnull);
|
2000-03-21 09:05:24 +03:00
|
|
|
};
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If we failed to find a cell, fall through to use originally-found element
|
|
|
|
} else {
|
2000-01-14 02:33:00 +03:00
|
|
|
// No table element -- set the background color on the body tag
|
|
|
|
res = nsEditor::GetBodyElement(getter_AddRefs(element));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!element) return NS_ERROR_NULL_POINTER;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
2000-01-14 02:33:00 +03:00
|
|
|
// Use the editor method that goes through the transaction system
|
|
|
|
return SetAttribute(element, "bgcolor", aColor);
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsHTMLEditor::SetBodyAttribute(const nsString& aAttribute, const nsString& aValue)
|
|
|
|
{
|
|
|
|
nsresult res;
|
|
|
|
// TODO: Check selection for Cell, Row, Column or table and do color on appropriate level
|
|
|
|
|
1999-08-25 14:51:55 +04:00
|
|
|
NS_ASSERTION(mDocWeak, "Missing Editor DOM Document");
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
// Set the background color attribute on the body tag
|
|
|
|
nsCOMPtr<nsIDOMElement> bodyElement;
|
|
|
|
|
|
|
|
res = nsEditor::GetBodyElement(getter_AddRefs(bodyElement));
|
1999-08-19 17:30:48 +04:00
|
|
|
if (!bodyElement) res = NS_ERROR_NULL_POINTER;
|
|
|
|
if (NS_SUCCEEDED(res))
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
1999-08-27 08:12:47 +04:00
|
|
|
// Use the editor method that goes through the transaction system
|
|
|
|
res = SetAttribute(bodyElement, aAttribute, aValue);
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
NS_IMETHODIMP nsHTMLEditor::MoveSelectionUp(nsIAtom *aIncrement, PRBool aExtendSelection)
|
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsHTMLEditor::MoveSelectionDown(nsIAtom *aIncrement, PRBool aExtendSelection)
|
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsHTMLEditor::MoveSelectionNext(nsIAtom *aIncrement, PRBool aExtendSelection)
|
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsHTMLEditor::MoveSelectionPrevious(nsIAtom *aIncrement, PRBool aExtendSelection)
|
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsHTMLEditor::SelectNext(nsIAtom *aIncrement, PRBool aExtendSelection)
|
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsHTMLEditor::SelectPrevious(nsIAtom *aIncrement, PRBool aExtendSelection)
|
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsHTMLEditor::ScrollUp(nsIAtom *aIncrement)
|
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsHTMLEditor::ScrollDown(nsIAtom *aIncrement)
|
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsHTMLEditor::ScrollIntoView(PRBool aScrollToBegin)
|
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
1999-09-09 03:32:04 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::GetDocumentIsEmpty(PRBool *aDocumentIsEmpty)
|
|
|
|
{
|
|
|
|
if (!aDocumentIsEmpty)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
if (!mRules)
|
|
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
|
|
|
|
return mRules->DocumentIsEmpty(aDocumentIsEmpty);
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::GetDocumentLength(PRInt32 *aCount)
|
|
|
|
{
|
2000-03-29 17:45:08 +04:00
|
|
|
if (!aCount) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
nsresult result;
|
|
|
|
// initialize out params
|
|
|
|
*aCount = 0;
|
|
|
|
|
|
|
|
// special-case for empty document, to account for the bogus text node
|
|
|
|
PRBool docEmpty;
|
|
|
|
result = GetDocumentIsEmpty(&docEmpty);
|
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
if (docEmpty)
|
|
|
|
{
|
|
|
|
*aCount = 0;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// get the body node
|
|
|
|
nsCOMPtr<nsIDOMElement> bodyElement;
|
|
|
|
result = nsEditor::GetBodyElement(getter_AddRefs(bodyElement));
|
|
|
|
if (NS_FAILED(result)) { return result; }
|
|
|
|
if (!bodyElement) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
|
|
|
|
// get the offsets of the first and last children of the body node
|
|
|
|
nsCOMPtr<nsIDOMNode>bodyNode = do_QueryInterface(bodyElement);
|
|
|
|
if (!bodyNode) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
PRInt32 numBodyChildren=0;
|
|
|
|
nsCOMPtr<nsIDOMNode>lastChild;
|
|
|
|
result = bodyNode->GetLastChild(getter_AddRefs(lastChild));
|
|
|
|
if (NS_FAILED(result)) { return result; }
|
|
|
|
if (!lastChild) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
result = GetChildOffset(lastChild, bodyNode, numBodyChildren);
|
|
|
|
if (NS_FAILED(result)) { return result; }
|
|
|
|
|
|
|
|
// count
|
|
|
|
PRInt32 start, end;
|
|
|
|
result = GetAbsoluteOffsetsForPoints(bodyNode, 0,
|
|
|
|
bodyNode, numBodyChildren,
|
|
|
|
bodyNode, start, end);
|
|
|
|
if (NS_SUCCEEDED(result))
|
|
|
|
{
|
|
|
|
NS_ASSERTION(0==start, "GetAbsoluteOffsetsForPoints failed to set start correctly.");
|
|
|
|
NS_ASSERTION(0<=end, "GetAbsoluteOffsetsForPoints failed to set end correctly.");
|
|
|
|
if (0<=end) {
|
|
|
|
*aCount = end;
|
|
|
|
printf ("count = %d\n", *aCount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsHTMLEditor::SetMaxTextLength(PRInt32 aMaxTextLength)
|
|
|
|
{
|
|
|
|
mMaxTextLength = aMaxTextLength;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsHTMLEditor::GetMaxTextLength(PRInt32& aMaxTextLength)
|
|
|
|
{
|
|
|
|
aMaxTextLength = mMaxTextLength;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef XP_MAC
|
|
|
|
#pragma mark -
|
2000-03-24 03:26:47 +03:00
|
|
|
#pragma mark nsIEditorStyleSheets methods
|
1999-08-09 05:37:50 +04:00
|
|
|
#pragma mark -
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::AddStyleSheet(nsICSSStyleSheet* aSheet)
|
|
|
|
{
|
1999-09-30 00:08:15 +04:00
|
|
|
AddStyleSheetTxn* txn;
|
|
|
|
nsresult rv = CreateTxnForAddStyleSheet(aSheet, &txn);
|
1999-08-19 17:30:48 +04:00
|
|
|
if (!txn) rv = NS_ERROR_NULL_POINTER;
|
1999-09-30 00:08:15 +04:00
|
|
|
if (NS_SUCCEEDED(rv))
|
|
|
|
{
|
|
|
|
rv = Do(txn);
|
|
|
|
if (NS_SUCCEEDED(rv))
|
|
|
|
{
|
|
|
|
mLastStyleSheet = do_QueryInterface(aSheet); // save it so we can remove before applying the next one
|
|
|
|
}
|
|
|
|
}
|
1999-11-01 18:15:35 +03:00
|
|
|
// The transaction system (if any) has taken ownwership of txns
|
|
|
|
NS_IF_RELEASE(txn);
|
1999-09-30 00:08:15 +04:00
|
|
|
|
|
|
|
return rv;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::RemoveStyleSheet(nsICSSStyleSheet* aSheet)
|
|
|
|
{
|
1999-09-30 00:08:15 +04:00
|
|
|
RemoveStyleSheetTxn* txn;
|
|
|
|
nsresult rv = CreateTxnForRemoveStyleSheet(aSheet, &txn);
|
1999-08-19 17:30:48 +04:00
|
|
|
if (!txn) rv = NS_ERROR_NULL_POINTER;
|
1999-09-30 00:08:15 +04:00
|
|
|
if (NS_SUCCEEDED(rv))
|
|
|
|
{
|
|
|
|
rv = Do(txn);
|
|
|
|
if (NS_SUCCEEDED(rv))
|
|
|
|
{
|
|
|
|
mLastStyleSheet = nsnull; // forget it
|
|
|
|
}
|
|
|
|
}
|
1999-11-01 18:15:35 +03:00
|
|
|
// The transaction system (if any) has taken ownwership of txns
|
|
|
|
NS_IF_RELEASE(txn);
|
1999-09-30 00:08:15 +04:00
|
|
|
|
|
|
|
return rv;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
|
2000-03-31 08:18:29 +04:00
|
|
|
// Do NOT use transaction system for override style sheets
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::RemoveOverrideStyleSheet(nsICSSStyleSheet* aSheet)
|
|
|
|
{
|
|
|
|
if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
|
|
|
|
if (!ps) return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDocument> document;
|
|
|
|
nsresult rv = ps->GetDocument(getter_AddRefs(document));
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if (!document) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIStyleSet> styleSet;
|
|
|
|
rv = ps->GetStyleSet(getter_AddRefs(styleSet));
|
|
|
|
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if (!styleSet) return NS_ERROR_NULL_POINTER;
|
|
|
|
nsCOMPtr<nsIStyleSheet> styleSheet = do_QueryInterface(aSheet);
|
|
|
|
if (!styleSheet) return NS_ERROR_NULL_POINTER;
|
|
|
|
styleSet->RemoveOverrideStyleSheet(styleSheet);
|
|
|
|
|
|
|
|
// This notifies document observers to rebuild all frames
|
|
|
|
// (this doesn't affect style sheet because it is not a doc sheet)
|
|
|
|
document->SetStyleSheetDisabledState(styleSheet, PR_FALSE);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-09-30 01:45:50 +04:00
|
|
|
NS_IMETHODIMP
|
2000-03-31 08:18:29 +04:00
|
|
|
nsHTMLEditor::ApplyOverrideStyleSheet(const nsString& aURL, nsICSSStyleSheet **aStyleSheet)
|
1999-09-30 01:45:50 +04:00
|
|
|
{
|
2000-03-31 08:18:29 +04:00
|
|
|
return ApplyDocumentOrOverrideStyleSheet(aURL, PR_TRUE, aStyleSheet);
|
1999-09-30 01:45:50 +04:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-09-30 01:45:50 +04:00
|
|
|
NS_IMETHODIMP
|
2000-03-31 08:18:29 +04:00
|
|
|
nsHTMLEditor::ApplyStyleSheet(const nsString& aURL, nsICSSStyleSheet **aStyleSheet)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
2000-03-31 08:18:29 +04:00
|
|
|
return ApplyDocumentOrOverrideStyleSheet(aURL, PR_FALSE, aStyleSheet);
|
1999-09-30 01:45:50 +04:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-09-30 01:45:50 +04:00
|
|
|
//Note: Loading a document style sheet is undoable, loading an override sheet is not
|
|
|
|
nsresult
|
2000-03-31 08:18:29 +04:00
|
|
|
nsHTMLEditor::ApplyDocumentOrOverrideStyleSheet(const nsString& aURL, PRBool aOverride, nsICSSStyleSheet **aStyleSheet)
|
1999-09-30 01:45:50 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsresult rv = NS_OK;
|
1999-09-30 01:45:50 +04:00
|
|
|
nsCOMPtr<nsIURI> uaURL;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-09-30 01:45:50 +04:00
|
|
|
rv = NS_NewURI(getter_AddRefs(uaURL), aURL);
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
nsCOMPtr<nsIDocument> document;
|
|
|
|
|
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;
|
|
|
|
rv = ps->GetDocument(getter_AddRefs(document));
|
2000-03-31 08:18:29 +04:00
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if (!document) return NS_ERROR_NULL_POINTER;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
2000-03-31 08:18:29 +04:00
|
|
|
nsCOMPtr<nsIHTMLContentContainer> container = do_QueryInterface(document);
|
|
|
|
if (!container) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
nsCOMPtr<nsICSSLoader> cssLoader;
|
|
|
|
nsCOMPtr<nsICSSStyleSheet> cssStyleSheet;
|
|
|
|
|
|
|
|
rv = container->GetCSSLoader(*getter_AddRefs(cssLoader));
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if (!cssLoader) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
PRBool complete;
|
1999-09-30 01:45:50 +04:00
|
|
|
|
2000-03-31 08:18:29 +04:00
|
|
|
if (aOverride)
|
|
|
|
{
|
|
|
|
// We use null for the callback and data pointer because
|
|
|
|
// we MUST ONLY load synchronous local files (no @import)
|
|
|
|
rv = cssLoader->LoadAgentSheet(uaURL, *getter_AddRefs(cssStyleSheet), complete,
|
|
|
|
nsnull);
|
|
|
|
|
|
|
|
// Synchronous loads should ALWAYS return completed
|
|
|
|
if (!complete || !cssStyleSheet)
|
1999-09-30 01:45:50 +04:00
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
2000-03-31 08:18:29 +04:00
|
|
|
nsCOMPtr<nsIStyleSheet> styleSheet;
|
|
|
|
styleSheet = do_QueryInterface(cssStyleSheet);
|
|
|
|
nsCOMPtr<nsIStyleSet> styleSet;
|
|
|
|
rv = ps->GetStyleSet(getter_AddRefs(styleSet));
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if (!styleSet) return NS_ERROR_NULL_POINTER;
|
1999-09-30 01:45:50 +04:00
|
|
|
|
2000-03-31 08:18:29 +04:00
|
|
|
// Add the override style sheet
|
|
|
|
// (This checks if already exists)
|
|
|
|
styleSet->AppendOverrideStyleSheet(styleSheet);
|
1999-08-09 05:37:50 +04:00
|
|
|
|
2000-03-31 08:18:29 +04:00
|
|
|
// This notifies document observers to rebuild all frames
|
|
|
|
// (this doesn't affect style sheet because it is not a doc sheet)
|
|
|
|
document->SetStyleSheetDisabledState(styleSheet, PR_FALSE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rv = cssLoader->LoadAgentSheet(uaURL, *getter_AddRefs(cssStyleSheet), complete,
|
|
|
|
this);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if (complete)
|
|
|
|
ApplyStyleSheetToPresShellDocument(cssStyleSheet,this);
|
|
|
|
|
|
|
|
//
|
|
|
|
// If not complete, we will be notified later
|
|
|
|
// with a call to ApplyStyleSheetToPresShellDocument().
|
|
|
|
//
|
|
|
|
}
|
|
|
|
if (aStyleSheet)
|
|
|
|
{
|
|
|
|
*aStyleSheet = cssStyleSheet;
|
|
|
|
NS_ADDREF(*aStyleSheet);
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef XP_MAC
|
|
|
|
#pragma mark -
|
2000-03-24 03:26:47 +03:00
|
|
|
#pragma mark nsIEditorMailSupport methods
|
1999-08-09 05:37:50 +04:00
|
|
|
#pragma mark -
|
|
|
|
#endif
|
|
|
|
|
1999-08-20 02:11:58 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::GetBodyStyleContext(nsIStyleContext** aStyleContext)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMElement> body;
|
|
|
|
nsresult res = GetBodyElement(getter_AddRefs(body));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
nsCOMPtr<nsIContent> content = do_QueryInterface(body);
|
|
|
|
|
|
|
|
nsIFrame *frame;
|
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;
|
|
|
|
res = ps->GetPrimaryFrameFor(content, &frame);
|
1999-08-20 02:11:58 +04:00
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIStyleContext> styleContext;
|
1999-08-25 14:51:55 +04:00
|
|
|
return ps->GetStyleContextFor(frame, aStyleContext);
|
1999-08-20 02:11:58 +04:00
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
//
|
|
|
|
// Get the wrap width for the first PRE tag in the document.
|
|
|
|
// If no PRE tag, throw an error.
|
|
|
|
//
|
|
|
|
NS_IMETHODIMP nsHTMLEditor::GetBodyWrapWidth(PRInt32 *aWrapColumn)
|
|
|
|
{
|
|
|
|
nsresult res;
|
|
|
|
|
|
|
|
if (! aWrapColumn)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
1999-08-20 02:11:58 +04:00
|
|
|
*aWrapColumn = -1; // default: no wrap
|
|
|
|
|
|
|
|
nsCOMPtr<nsIStyleContext> styleContext;
|
|
|
|
res = GetBodyStyleContext(getter_AddRefs(styleContext));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
const nsStyleText* styleText =
|
|
|
|
(const nsStyleText*)styleContext->GetStyleData(eStyleStruct_Text);
|
|
|
|
|
|
|
|
if (NS_STYLE_WHITESPACE_PRE == styleText->mWhiteSpace)
|
|
|
|
*aWrapColumn = 0; // wrap to window width
|
|
|
|
else if (NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == styleText->mWhiteSpace)
|
|
|
|
{
|
|
|
|
const nsStylePosition* stylePosition =
|
|
|
|
(const nsStylePosition*)styleContext->GetStyleData(eStyleStruct_Position);
|
|
|
|
if (stylePosition->mWidth.GetUnit() == eStyleUnit_Chars)
|
|
|
|
*aWrapColumn = stylePosition->mWidth.GetIntValue();
|
|
|
|
else {
|
|
|
|
#ifdef DEBUG_akkana
|
1999-09-14 01:39:51 +04:00
|
|
|
printf("Can't get wrap column: style unit is %d\n",
|
1999-08-20 02:11:58 +04:00
|
|
|
stylePosition->mWidth.GetUnit());
|
|
|
|
#endif
|
|
|
|
*aWrapColumn = -1;
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
*aWrapColumn = -1;
|
|
|
|
return NS_OK;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
|
2000-02-05 02:39:31 +03:00
|
|
|
//
|
|
|
|
// See if the style value includes this attribute, and if it does,
|
|
|
|
// cut out everything from the attribute to the next semicolon.
|
|
|
|
//
|
|
|
|
static void CutStyle(const char* stylename, nsString& styleValue)
|
|
|
|
{
|
|
|
|
// Find the current wrapping type:
|
|
|
|
PRInt32 styleStart = styleValue.Find(stylename, PR_TRUE);
|
|
|
|
if (styleStart >= 0)
|
|
|
|
{
|
|
|
|
PRInt32 styleEnd = styleValue.Find(";", PR_FALSE, styleStart);
|
|
|
|
if (styleEnd > styleStart)
|
|
|
|
styleValue.Cut(styleStart, styleEnd - styleStart + 1);
|
|
|
|
else
|
|
|
|
styleValue.Cut(styleStart, styleValue.Length() - styleStart);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
//
|
|
|
|
// Change the wrap width on the first <PRE> tag in this document.
|
|
|
|
// (Eventually want to search for more than one in case there are
|
|
|
|
// interspersed quoted text blocks.)
|
1999-08-20 02:11:58 +04:00
|
|
|
// Alternately: Change the wrap width on the editor style sheet.
|
1999-08-09 05:37:50 +04:00
|
|
|
//
|
|
|
|
NS_IMETHODIMP nsHTMLEditor::SetBodyWrapWidth(PRInt32 aWrapColumn)
|
|
|
|
{
|
|
|
|
nsresult res;
|
|
|
|
|
1999-08-21 02:39:48 +04:00
|
|
|
// Ought to set a style sheet here ...
|
|
|
|
// Probably should keep around an mPlaintextStyleSheet for this purpose.
|
|
|
|
nsCOMPtr<nsIDOMElement> bodyElement;
|
|
|
|
res = GetBodyElement(getter_AddRefs(bodyElement));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!bodyElement) return NS_ERROR_NULL_POINTER;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-08-21 02:39:48 +04:00
|
|
|
// Get the current style for this body element:
|
|
|
|
nsAutoString styleName ("style");
|
1999-09-15 03:40:16 +04:00
|
|
|
nsAutoString styleValue;
|
1999-08-21 02:39:48 +04:00
|
|
|
res = bodyElement->GetAttribute(styleName, styleValue);
|
|
|
|
if (NS_FAILED(res)) return res;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
2000-02-05 02:39:31 +03:00
|
|
|
// We'll replace styles for these values:
|
|
|
|
CutStyle("white-space", styleValue);
|
|
|
|
CutStyle("width", styleValue);
|
|
|
|
CutStyle("font-family", styleValue);
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-08-21 02:39:48 +04:00
|
|
|
// If we have other style left, trim off any existing semicolons
|
|
|
|
// or whitespace, then add a known semicolon-space:
|
|
|
|
if (styleValue.Length() > 0)
|
|
|
|
{
|
|
|
|
styleValue.Trim("; \t", PR_FALSE, PR_TRUE);
|
|
|
|
styleValue.Append("; ");
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
|
2000-02-05 02:39:31 +03:00
|
|
|
// Make sure we have fixed-width font. This should be done for us,
|
2000-03-14 05:58:14 +03:00
|
|
|
// but it isn't, see bug 22502, so we have to add "font: monospace;".
|
|
|
|
// Only do this if we're wrapping.
|
|
|
|
if (aWrapColumn >= 0)
|
|
|
|
styleValue.Append("font-family: monospace; ");
|
2000-02-05 02:39:31 +03:00
|
|
|
|
1999-08-21 02:39:48 +04:00
|
|
|
// and now we're ready to set the new whitespace/wrapping style.
|
|
|
|
if (aWrapColumn > 0) // Wrap to a fixed column
|
|
|
|
{
|
|
|
|
styleValue.Append("white-space: -moz-pre-wrap; width: ");
|
|
|
|
styleValue.Append(aWrapColumn);
|
|
|
|
styleValue.Append("ch;");
|
|
|
|
}
|
|
|
|
else if (aWrapColumn == 0)
|
2000-02-05 02:39:31 +03:00
|
|
|
styleValue.Append("white-space: -moz-pre-wrap;");
|
1999-08-21 02:39:48 +04:00
|
|
|
else
|
2000-02-05 02:39:31 +03:00
|
|
|
styleValue.Append("white-space: pre;");
|
|
|
|
|
|
|
|
res = bodyElement->SetAttribute(styleName, styleValue);
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-08-21 02:39:48 +04:00
|
|
|
#ifdef DEBUG_akkana
|
|
|
|
char* curstyle = styleValue.ToNewCString();
|
2000-02-05 02:39:31 +03:00
|
|
|
printf("Setting style: [%s]\nNow body looks like:\n", curstyle);
|
|
|
|
Recycle(curstyle);
|
|
|
|
//nsCOMPtr<nsIContent> nodec (do_QueryInterface(bodyElement));
|
|
|
|
//if (nodec) nodec->List(stdout);
|
|
|
|
//printf("-----\n");
|
1999-08-21 02:39:48 +04:00
|
|
|
#endif /* DEBUG_akkana */
|
1999-08-09 05:37:50 +04:00
|
|
|
|
2000-02-05 02:39:31 +03:00
|
|
|
return res;
|
1999-08-21 02:39:48 +04:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::GetEmbeddedObjects(nsISupportsArray** aNodeList)
|
1999-04-20 21:47:12 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
if (!aNodeList)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
nsresult res;
|
|
|
|
|
|
|
|
res = NS_NewISupportsArray(aNodeList);
|
1999-08-19 17:30:48 +04:00
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!*aNodeList) return NS_ERROR_NULL_POINTER;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
nsCOMPtr<nsIContentIterator> iter;
|
|
|
|
res = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull,
|
2000-02-01 17:26:27 +03:00
|
|
|
NS_GET_IID(nsIContentIterator),
|
1999-08-09 05:37:50 +04:00
|
|
|
getter_AddRefs(iter));
|
1999-08-19 17:30:48 +04:00
|
|
|
if (!iter) return NS_ERROR_NULL_POINTER;
|
|
|
|
if ((NS_SUCCEEDED(res)))
|
1999-04-20 21:47:12 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
// get the root content
|
|
|
|
nsCOMPtr<nsIContent> rootContent;
|
1999-05-05 08:05:19 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMDocument> domdoc;
|
|
|
|
nsEditor::GetDocument(getter_AddRefs(domdoc));
|
|
|
|
if (!domdoc)
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
1999-05-05 08:05:19 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDocument> doc (do_QueryInterface(domdoc));
|
|
|
|
if (!doc)
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
|
|
|
|
rootContent = doc->GetRootContent();
|
|
|
|
|
|
|
|
iter->Init(rootContent);
|
|
|
|
|
|
|
|
// loop through the content iterator for each content node
|
1999-10-28 07:16:48 +04:00
|
|
|
while (NS_ENUMERATOR_FALSE == iter->IsDone())
|
1999-04-20 21:47:12 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIContent> content;
|
|
|
|
res = iter->CurrentNode(getter_AddRefs(content));
|
|
|
|
if (NS_FAILED(res))
|
|
|
|
break;
|
|
|
|
nsCOMPtr<nsIDOMNode> node (do_QueryInterface(content));
|
|
|
|
if (node)
|
1999-04-20 21:47:12 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsAutoString tagName;
|
|
|
|
node->GetNodeName(tagName);
|
|
|
|
tagName.ToLowerCase();
|
|
|
|
|
|
|
|
// See if it's an image or an embed
|
2000-03-26 15:39:08 +04:00
|
|
|
if (tagName.Equals("img") || tagName.Equals("embed"))
|
1999-08-09 05:37:50 +04:00
|
|
|
(*aNodeList)->AppendElement(node);
|
2000-03-26 15:39:08 +04:00
|
|
|
else if (tagName.Equals("a"))
|
1999-04-21 08:08:43 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
// XXX Only include links if they're links to file: URLs
|
|
|
|
nsCOMPtr<nsIDOMHTMLAnchorElement> anchor (do_QueryInterface(content));
|
|
|
|
if (anchor)
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsAutoString href;
|
|
|
|
if (NS_SUCCEEDED(anchor->GetHref(href)))
|
|
|
|
if (href.Compare("file:", PR_TRUE, 5) == 0)
|
|
|
|
(*aNodeList)->AppendElement(node);
|
1999-05-05 08:05:19 +04:00
|
|
|
}
|
1999-04-21 08:08:43 +04:00
|
|
|
}
|
1999-04-20 21:47:12 +04:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
iter->Next();
|
1999-04-20 21:47:12 +04:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef XP_MAC
|
|
|
|
#pragma mark -
|
2000-03-24 03:26:47 +03:00
|
|
|
#pragma mark nsIEditor overrides
|
1999-08-09 05:37:50 +04:00
|
|
|
#pragma mark -
|
|
|
|
#endif
|
|
|
|
|
1999-09-19 14:32:21 +04:00
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::Undo(PRUint32 aCount)
|
|
|
|
{
|
1999-12-10 01:37:36 +03:00
|
|
|
ForceCompositionEnd();
|
1999-09-19 14:32:21 +04:00
|
|
|
nsresult result = NS_OK;
|
|
|
|
|
|
|
|
BeginUpdateViewBatch();
|
|
|
|
|
|
|
|
nsTextRulesInfo ruleInfo(nsTextEditRules::kUndo);
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
|
|
|
GetSelection(getter_AddRefs(selection));
|
1999-10-06 23:34:09 +04:00
|
|
|
PRBool cancel, handled;
|
|
|
|
result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
|
1999-09-19 14:32:21 +04:00
|
|
|
|
|
|
|
if (!cancel && NS_SUCCEEDED(result))
|
|
|
|
{
|
|
|
|
result = nsEditor::Undo(aCount);
|
|
|
|
result = mRules->DidDoAction(selection, &ruleInfo, result);
|
|
|
|
}
|
|
|
|
|
|
|
|
EndUpdateViewBatch();
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::Redo(PRUint32 aCount)
|
|
|
|
{
|
|
|
|
nsresult result = NS_OK;
|
|
|
|
|
|
|
|
BeginUpdateViewBatch();
|
|
|
|
|
|
|
|
nsTextRulesInfo ruleInfo(nsTextEditRules::kRedo);
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
|
|
|
GetSelection(getter_AddRefs(selection));
|
1999-10-06 23:34:09 +04:00
|
|
|
PRBool cancel, handled;
|
|
|
|
result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
|
1999-09-19 14:32:21 +04:00
|
|
|
|
|
|
|
if (!cancel && NS_SUCCEEDED(result))
|
|
|
|
{
|
|
|
|
result = nsEditor::Redo(aCount);
|
|
|
|
result = mRules->DidDoAction(selection, &ruleInfo, result);
|
|
|
|
}
|
|
|
|
|
|
|
|
EndUpdateViewBatch();
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP nsHTMLEditor::Cut()
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
2000-01-19 00:50:15 +03:00
|
|
|
nsresult res = GetSelection(getter_AddRefs(selection));
|
1999-08-09 05:37:50 +04:00
|
|
|
if (!NS_SUCCEEDED(res))
|
|
|
|
return res;
|
|
|
|
|
|
|
|
PRBool isCollapsed;
|
|
|
|
if (NS_SUCCEEDED(selection->GetIsCollapsed(&isCollapsed)) && isCollapsed)
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
|
|
|
|
res = Copy();
|
|
|
|
if (NS_SUCCEEDED(res))
|
1999-12-07 11:30:19 +03:00
|
|
|
res = DeleteSelection(eNone);
|
1999-08-09 05:37:50 +04:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2000-01-19 00:50:15 +03:00
|
|
|
NS_IMETHODIMP nsHTMLEditor::CanCut(PRBool &aCanCut)
|
|
|
|
{
|
|
|
|
aCanCut = PR_FALSE;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
|
|
|
nsresult res = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
PRBool isCollapsed;
|
|
|
|
res = selection->GetIsCollapsed(&isCollapsed);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
aCanCut = !isCollapsed && IsModifiable();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP nsHTMLEditor::Copy()
|
|
|
|
{
|
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;
|
|
|
|
return ps->DoCopy();
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
|
2000-01-19 00:50:15 +03:00
|
|
|
NS_IMETHODIMP nsHTMLEditor::CanCopy(PRBool &aCanCopy)
|
|
|
|
{
|
|
|
|
aCanCopy = PR_FALSE;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
|
|
|
nsresult res = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
PRBool isCollapsed;
|
|
|
|
res = selection->GetIsCollapsed(&isCollapsed);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
aCanCopy = !isCollapsed;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP nsHTMLEditor::Paste()
|
|
|
|
{
|
1999-12-10 01:37:36 +03:00
|
|
|
ForceCompositionEnd();
|
1999-09-02 21:56:09 +04:00
|
|
|
nsAutoString stuffToPaste;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
// Get Clipboard Service
|
1999-08-25 12:35:06 +04:00
|
|
|
nsresult rv;
|
|
|
|
NS_WITH_SERVICE ( nsIClipboard, clipboard, kCClipboardCID, &rv );
|
|
|
|
if ( NS_FAILED(rv) )
|
|
|
|
return rv;
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
// Create generic Transferable for getting the data
|
|
|
|
nsCOMPtr<nsITransferable> trans;
|
|
|
|
rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull,
|
2000-02-01 17:26:27 +03:00
|
|
|
NS_GET_IID(nsITransferable),
|
1999-08-09 05:37:50 +04:00
|
|
|
(void**) getter_AddRefs(trans));
|
|
|
|
if (NS_SUCCEEDED(rv))
|
|
|
|
{
|
|
|
|
// Get the nsITransferable interface for getting the data from the clipboard
|
|
|
|
if (trans)
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
// Create the desired DataFlavor for the type of data
|
|
|
|
// we want to get out of the transferable
|
|
|
|
if ((mFlags & eEditorPlaintextMask) == 0) // This should only happen in html editors, not plaintext
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
1999-08-25 12:35:06 +04:00
|
|
|
trans->AddDataFlavor(kJPEGImageMime);
|
|
|
|
trans->AddDataFlavor(kHTMLMime);
|
1999-05-05 08:05:19 +04:00
|
|
|
}
|
1999-11-11 02:42:11 +03:00
|
|
|
trans->AddDataFlavor(kUnicodeMime);
|
1999-05-05 08:05:19 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
// Get the Data from the clipboard
|
|
|
|
if (NS_SUCCEEDED(clipboard->GetData(trans)))
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
1999-08-25 12:35:06 +04:00
|
|
|
char* bestFlavor = nsnull;
|
|
|
|
nsCOMPtr<nsISupports> genericDataObj;
|
|
|
|
PRUint32 len = 0;
|
|
|
|
if ( NS_SUCCEEDED(trans->GetAnyTransferData(&bestFlavor, getter_AddRefs(genericDataObj), &len)) )
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
1999-08-25 12:35:06 +04:00
|
|
|
nsAutoString flavor ( bestFlavor ); // just so we can use flavor.Equals()
|
1999-08-09 05:37:50 +04:00
|
|
|
#ifdef DEBUG_akkana
|
1999-11-11 02:42:11 +03:00
|
|
|
printf("Got flavor [%s]\n", bestFlavor);
|
1999-08-09 05:37:50 +04:00
|
|
|
#endif
|
1999-08-25 12:35:06 +04:00
|
|
|
if (flavor.Equals(kHTMLMime))
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
1999-08-25 12:35:06 +04:00
|
|
|
nsCOMPtr<nsISupportsWString> textDataObj ( do_QueryInterface(genericDataObj) );
|
|
|
|
if (textDataObj && len > 0)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
1999-08-25 12:35:06 +04:00
|
|
|
PRUnichar* text = nsnull;
|
1999-08-25 13:15:18 +04:00
|
|
|
textDataObj->ToString ( &text );
|
2000-03-12 23:39:41 +03:00
|
|
|
stuffToPaste.Assign ( text, len / 2 );
|
1999-09-12 05:31:57 +04:00
|
|
|
nsAutoEditBatch beginBatching(this);
|
1999-08-09 05:37:50 +04:00
|
|
|
rv = InsertHTML(stuffToPaste);
|
1999-05-05 08:05:19 +04:00
|
|
|
}
|
|
|
|
}
|
1999-11-11 02:42:11 +03:00
|
|
|
else if (flavor.Equals(kUnicodeMime))
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsISupportsWString> textDataObj ( do_QueryInterface(genericDataObj) );
|
|
|
|
if (textDataObj && len > 0)
|
|
|
|
{
|
|
|
|
PRUnichar* text = nsnull;
|
|
|
|
textDataObj->ToString ( &text );
|
2000-03-12 23:39:41 +03:00
|
|
|
stuffToPaste.Assign ( text, len / 2 );
|
1999-11-11 02:42:11 +03:00
|
|
|
nsAutoEditBatch beginBatching(this);
|
|
|
|
rv = InsertText(stuffToPaste);
|
|
|
|
}
|
|
|
|
}
|
1999-08-25 12:35:06 +04:00
|
|
|
else if (flavor.Equals(kJPEGImageMime))
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
|
|
|
// Insert Image code here
|
|
|
|
printf("Don't know how to insert an image yet!\n");
|
|
|
|
//nsIImage* image = (nsIImage *)data;
|
|
|
|
//NS_RELEASE(image);
|
1999-08-20 02:11:58 +04:00
|
|
|
rv = NS_ERROR_NOT_IMPLEMENTED; // for now give error code
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
1999-05-05 08:05:19 +04:00
|
|
|
}
|
1999-09-06 10:22:51 +04:00
|
|
|
nsCRT::free(bestFlavor);
|
1999-05-05 08:05:19 +04:00
|
|
|
}
|
|
|
|
}
|
1999-04-20 21:47:12 +04:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
|
2000-03-28 04:34:26 +04:00
|
|
|
// Try to scroll the selection into view if the paste succeeded:
|
|
|
|
if (NS_SUCCEEDED(rv))
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIPresShell> presShell;
|
|
|
|
if (NS_SUCCEEDED(GetPresShell(getter_AddRefs(presShell))) && presShell)
|
|
|
|
presShell->ScrollSelectionIntoView(SELECTION_NORMAL, SELECTION_FOCUS_REGION);
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
return rv;
|
1999-04-20 21:47:12 +04:00
|
|
|
}
|
|
|
|
|
2000-01-19 00:50:15 +03:00
|
|
|
|
|
|
|
NS_IMETHODIMP nsHTMLEditor::CanPaste(PRBool &aCanPaste)
|
|
|
|
{
|
|
|
|
aCanPaste = PR_FALSE;
|
|
|
|
|
|
|
|
nsresult rv;
|
|
|
|
NS_WITH_SERVICE(nsIClipboard, clipboard, kCClipboardCID, &rv);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
// the flavors that we can deal with
|
2000-02-02 01:26:21 +03:00
|
|
|
char* textEditorFlavors[] = { kUnicodeMime, nsnull };
|
2000-01-19 00:50:15 +03:00
|
|
|
char* htmlEditorFlavors[] = { kJPEGImageMime, kHTMLMime, nsnull };
|
|
|
|
|
|
|
|
nsCOMPtr<nsISupportsArray> flavorsList;
|
|
|
|
rv = nsComponentManager::CreateInstance(NS_SUPPORTSARRAY_PROGID, nsnull,
|
|
|
|
NS_GET_IID(nsISupportsArray), getter_AddRefs(flavorsList));
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
PRUint32 editorFlags;
|
|
|
|
GetFlags(&editorFlags);
|
|
|
|
|
|
|
|
// add the flavors for all editors
|
|
|
|
for (char** flavor = textEditorFlavors; *flavor; flavor++)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsISupportsString> flavorString;
|
|
|
|
nsComponentManager::CreateInstance(NS_SUPPORTS_STRING_PROGID, nsnull,
|
|
|
|
NS_GET_IID(nsISupportsString), getter_AddRefs(flavorString));
|
|
|
|
if (flavorString)
|
|
|
|
{
|
|
|
|
flavorString->SetData(*flavor);
|
|
|
|
flavorsList->AppendElement(flavorString);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// add the HTML-editor only flavors
|
|
|
|
if ((editorFlags & eEditorPlaintextMask) == 0)
|
|
|
|
{
|
2000-01-26 03:57:37 +03:00
|
|
|
for (char** htmlFlavor = htmlEditorFlavors; *htmlFlavor; htmlFlavor++)
|
2000-01-19 00:50:15 +03:00
|
|
|
{
|
|
|
|
nsCOMPtr<nsISupportsString> flavorString;
|
|
|
|
nsComponentManager::CreateInstance(NS_SUPPORTS_STRING_PROGID, nsnull,
|
|
|
|
NS_GET_IID(nsISupportsString), getter_AddRefs(flavorString));
|
|
|
|
if (flavorString)
|
|
|
|
{
|
2000-01-26 03:57:37 +03:00
|
|
|
flavorString->SetData(*htmlFlavor);
|
2000-01-19 00:50:15 +03:00
|
|
|
flavorsList->AppendElement(flavorString);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool haveFlavors;
|
|
|
|
rv = clipboard->HasDataMatchingFlavors(flavorsList, &haveFlavors);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
aCanPaste = haveFlavors;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-11-11 02:42:11 +03:00
|
|
|
//
|
|
|
|
// HTML PasteAsQuotation: Paste in a blockquote type=cite
|
|
|
|
//
|
|
|
|
NS_IMETHODIMP nsHTMLEditor::PasteAsQuotation()
|
|
|
|
{
|
|
|
|
if (mFlags & eEditorPlaintextMask)
|
|
|
|
return PasteAsPlaintextQuotation();
|
|
|
|
|
|
|
|
nsAutoString citation("");
|
|
|
|
return PasteAsCitedQuotation(citation);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsHTMLEditor::PasteAsCitedQuotation(const nsString& aCitation)
|
|
|
|
{
|
|
|
|
nsAutoEditBatch beginBatching(this);
|
2000-03-31 02:26:06 +04:00
|
|
|
nsAutoRules beginRulesSniffing(this, kOpInsertQuotation, nsIEditor::eNext);
|
1999-11-11 02:42:11 +03:00
|
|
|
|
2000-01-10 13:13:58 +03:00
|
|
|
// get selection
|
1999-11-11 02:42:11 +03:00
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
2000-01-10 13:13:58 +03:00
|
|
|
nsresult res = GetSelection(getter_AddRefs(selection));
|
1999-11-11 02:42:11 +03:00
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!selection) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
2000-01-10 13:13:58 +03:00
|
|
|
// give rules a chance to handle or cancel
|
|
|
|
nsTextRulesInfo ruleInfo(nsHTMLEditRules::kInsertElement);
|
|
|
|
PRBool cancel, handled;
|
|
|
|
res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (cancel) return NS_OK; // rules canceled the operation
|
|
|
|
if (!handled)
|
1999-11-11 02:42:11 +03:00
|
|
|
{
|
2000-01-10 13:13:58 +03:00
|
|
|
nsCOMPtr<nsIDOMNode> newNode;
|
|
|
|
nsAutoString tag("blockquote");
|
|
|
|
res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!newNode) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
// Try to set type=cite. Ignore it if this fails.
|
|
|
|
nsCOMPtr<nsIDOMElement> newElement (do_QueryInterface(newNode));
|
|
|
|
if (newElement)
|
|
|
|
{
|
|
|
|
nsAutoString type ("type");
|
|
|
|
nsAutoString cite ("cite");
|
|
|
|
newElement->SetAttribute(type, cite);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the selection to the underneath the node we just inserted:
|
|
|
|
res = selection->Collapse(newNode, 0);
|
|
|
|
if (NS_FAILED(res))
|
|
|
|
{
|
1999-11-11 02:42:11 +03:00
|
|
|
#ifdef DEBUG_akkana
|
2000-01-10 13:13:58 +03:00
|
|
|
printf("Couldn't collapse");
|
1999-11-11 02:42:11 +03:00
|
|
|
#endif
|
2000-01-10 13:13:58 +03:00
|
|
|
// XXX: error result: should res be returned here?
|
|
|
|
}
|
1999-11-11 02:42:11 +03:00
|
|
|
|
2000-01-10 13:13:58 +03:00
|
|
|
res = Paste();
|
|
|
|
}
|
1999-11-11 02:42:11 +03:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Paste a plaintext quotation
|
|
|
|
//
|
|
|
|
NS_IMETHODIMP nsHTMLEditor::PasteAsPlaintextQuotation()
|
|
|
|
{
|
|
|
|
// Get Clipboard Service
|
|
|
|
nsresult rv;
|
|
|
|
NS_WITH_SERVICE(nsIClipboard, clipboard, kCClipboardCID, &rv);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
// Create generic Transferable for getting the data
|
|
|
|
nsCOMPtr<nsITransferable> trans;
|
|
|
|
rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull,
|
2000-02-01 17:26:27 +03:00
|
|
|
NS_GET_IID(nsITransferable),
|
1999-11-11 02:42:11 +03:00
|
|
|
(void**) getter_AddRefs(trans));
|
|
|
|
if (NS_SUCCEEDED(rv) && trans)
|
|
|
|
{
|
|
|
|
// We only handle plaintext pastes here
|
|
|
|
trans->AddDataFlavor(kUnicodeMime);
|
|
|
|
|
|
|
|
// Get the Data from the clipboard
|
|
|
|
clipboard->GetData(trans);
|
|
|
|
|
|
|
|
// Now we ask the transferable for the data
|
|
|
|
// it still owns the data, we just have a pointer to it.
|
|
|
|
// If it can't support a "text" output of the data the call will fail
|
|
|
|
nsCOMPtr<nsISupports> genericDataObj;
|
|
|
|
PRUint32 len = 0;
|
|
|
|
char* flav = 0;
|
|
|
|
rv = trans->GetAnyTransferData(&flav, getter_AddRefs(genericDataObj),
|
|
|
|
&len);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
{
|
|
|
|
#ifdef DEBUG_akkana
|
|
|
|
printf("PasteAsPlaintextQuotation: GetAnyTransferData failed, %d\n", rv);
|
|
|
|
#endif
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_akkana
|
|
|
|
printf("Got flavor [%s]\n", flav);
|
|
|
|
#endif
|
|
|
|
nsAutoString flavor(flav);
|
|
|
|
nsAutoString stuffToPaste;
|
2000-02-02 01:26:21 +03:00
|
|
|
if (flavor.Equals(kUnicodeMime))
|
1999-11-11 02:42:11 +03:00
|
|
|
{
|
|
|
|
nsCOMPtr<nsISupportsWString> textDataObj ( do_QueryInterface(genericDataObj) );
|
|
|
|
if (textDataObj && len > 0)
|
|
|
|
{
|
|
|
|
PRUnichar* text = nsnull;
|
|
|
|
textDataObj->ToString ( &text );
|
2000-03-12 23:39:41 +03:00
|
|
|
stuffToPaste.Assign ( text, len / 2 );
|
1999-11-11 02:42:11 +03:00
|
|
|
nsAutoEditBatch beginBatching(this);
|
1999-11-24 23:48:59 +03:00
|
|
|
rv = InsertAsPlaintextQuotation(stuffToPaste, 0);
|
1999-11-11 02:42:11 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
nsCRT::free(flav);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
1999-11-24 23:48:59 +03:00
|
|
|
NS_IMETHODIMP nsHTMLEditor::InsertAsQuotation(const nsString& aQuotedText,
|
|
|
|
nsIDOMNode **aNodeInserted)
|
1999-11-11 02:42:11 +03:00
|
|
|
{
|
|
|
|
if (mFlags & eEditorPlaintextMask)
|
1999-11-24 23:48:59 +03:00
|
|
|
return InsertAsPlaintextQuotation(aQuotedText, aNodeInserted);
|
1999-11-11 02:42:11 +03:00
|
|
|
|
|
|
|
nsAutoString citation ("");
|
2000-01-04 23:38:12 +03:00
|
|
|
nsAutoString charset ("");
|
2000-03-21 02:13:25 +03:00
|
|
|
return InsertAsCitedQuotation(aQuotedText, citation, PR_FALSE,
|
|
|
|
charset, aNodeInserted);
|
1999-11-11 02:42:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// text insert.
|
1999-11-24 23:48:59 +03:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::InsertAsPlaintextQuotation(const nsString& aQuotedText,
|
|
|
|
nsIDOMNode **aNodeInserted)
|
1999-11-11 02:42:11 +03:00
|
|
|
{
|
2000-02-25 07:39:30 +03:00
|
|
|
// We have the text. Cite it appropriately:
|
1999-11-11 02:42:11 +03:00
|
|
|
nsCOMPtr<nsICiter> citer;
|
|
|
|
nsresult rv;
|
|
|
|
NS_WITH_SERVICE(nsIPref, prefs, kPrefServiceCID, &rv);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
char *citationType = 0;
|
|
|
|
rv = prefs->CopyCharPref("mail.compose.citationType", &citationType);
|
|
|
|
|
|
|
|
if (NS_SUCCEEDED(rv) && citationType[0])
|
|
|
|
{
|
|
|
|
if (!strncmp(citationType, "aol", 3))
|
|
|
|
citer = new nsAOLCiter;
|
|
|
|
else
|
|
|
|
citer = new nsInternetCiter;
|
|
|
|
PL_strfree(citationType);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
citer = new nsInternetCiter;
|
|
|
|
|
|
|
|
// Let the citer quote it for us:
|
|
|
|
nsString quotedStuff;
|
|
|
|
rv = citer->GetCiteString(aQuotedText, quotedStuff);
|
|
|
|
if (!NS_SUCCEEDED(rv))
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
nsAutoEditBatch beginBatching(this);
|
2000-02-25 07:39:30 +03:00
|
|
|
nsAutoRules beginRulesSniffing(this, kOpInsertQuotation, nsIEditor::eNext);
|
1999-11-13 03:26:45 +03:00
|
|
|
|
2000-01-10 13:13:58 +03:00
|
|
|
// get selection
|
1999-11-30 00:15:57 +03:00
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
|
|
|
rv = GetSelection(getter_AddRefs(selection));
|
2000-01-10 13:13:58 +03:00
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if (!selection) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
// give rules a chance to handle or cancel
|
|
|
|
nsTextRulesInfo ruleInfo(nsHTMLEditRules::kInsertElement);
|
|
|
|
PRBool cancel, handled;
|
|
|
|
rv = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if (cancel) return NS_OK; // rules canceled the operation
|
2000-02-25 07:39:30 +03:00
|
|
|
nsCOMPtr<nsIDOMNode> preNode;
|
2000-01-10 13:13:58 +03:00
|
|
|
if (!handled)
|
1999-11-13 03:26:45 +03:00
|
|
|
{
|
2000-01-10 13:13:58 +03:00
|
|
|
// Wrap the inserted quote in a <pre> so it won't be wrapped:
|
|
|
|
nsAutoString tag("pre");
|
|
|
|
rv = DeleteSelectionAndCreateNode(tag, getter_AddRefs(preNode));
|
|
|
|
|
|
|
|
// If this succeeded, then set selection inside the pre
|
|
|
|
// so the inserted text will end up there.
|
|
|
|
// If it failed, we don't care what the return value was,
|
|
|
|
// but we'll fall through and try to insert the text anyway.
|
|
|
|
if (NS_SUCCEEDED(rv) && preNode)
|
|
|
|
{
|
2000-02-25 07:39:30 +03:00
|
|
|
// Add an attribute on the pre node so we'll know it's a quotation.
|
|
|
|
// Do this after the insertion, so that
|
|
|
|
nsCOMPtr<nsIDOMElement> preElement (do_QueryInterface(preNode));
|
|
|
|
if (preElement)
|
|
|
|
preElement->SetAttribute("_moz_quote", "true");
|
|
|
|
|
|
|
|
// and set the selection inside it:
|
1999-11-13 03:26:45 +03:00
|
|
|
selection->Collapse(preNode, 0);
|
2000-01-10 13:13:58 +03:00
|
|
|
}
|
1999-11-13 03:26:45 +03:00
|
|
|
|
2000-01-10 13:13:58 +03:00
|
|
|
rv = InsertText(quotedStuff);
|
2000-02-25 07:39:30 +03:00
|
|
|
|
|
|
|
if (aNodeInserted && NS_SUCCEEDED(rv))
|
1999-11-24 23:48:59 +03:00
|
|
|
{
|
2000-02-25 07:39:30 +03:00
|
|
|
*aNodeInserted = preNode;
|
|
|
|
NS_IF_ADDREF(*aNodeInserted);
|
1999-11-24 23:48:59 +03:00
|
|
|
}
|
1999-11-30 00:15:57 +03:00
|
|
|
|
2000-01-10 13:13:58 +03:00
|
|
|
// Set the selection to just after the inserted node:
|
|
|
|
if (NS_SUCCEEDED(rv) && preNode)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> parent;
|
|
|
|
PRInt32 offset;
|
|
|
|
if (NS_SUCCEEDED(GetNodeLocation(preNode, &parent, &offset)) && parent)
|
|
|
|
selection->Collapse(parent, offset+1);
|
|
|
|
}
|
1999-11-30 00:15:57 +03:00
|
|
|
}
|
1999-11-24 23:48:59 +03:00
|
|
|
return rv;
|
1999-11-11 02:42:11 +03:00
|
|
|
}
|
|
|
|
|
1999-11-24 23:48:59 +03:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::InsertAsCitedQuotation(const nsString& aQuotedText,
|
|
|
|
const nsString& aCitation,
|
2000-03-21 02:13:25 +03:00
|
|
|
PRBool aInsertHTML,
|
2000-01-04 23:38:12 +03:00
|
|
|
const nsString& aCharset,
|
1999-11-24 23:48:59 +03:00
|
|
|
nsIDOMNode **aNodeInserted)
|
1999-11-11 02:42:11 +03:00
|
|
|
{
|
|
|
|
nsAutoEditBatch beginBatching(this);
|
|
|
|
nsCOMPtr<nsIDOMNode> newNode;
|
2000-02-25 07:39:30 +03:00
|
|
|
nsAutoRules beginRulesSniffing(this, kOpInsertQuotation, nsIEditor::eNext);
|
1999-11-11 02:42:11 +03:00
|
|
|
|
2000-01-10 13:13:58 +03:00
|
|
|
// get selection
|
1999-11-30 00:15:57 +03:00
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
2000-01-10 13:13:58 +03:00
|
|
|
nsresult res = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!selection) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
// give rules a chance to handle or cancel
|
|
|
|
nsTextRulesInfo ruleInfo(nsHTMLEditRules::kInsertElement);
|
|
|
|
PRBool cancel, handled;
|
|
|
|
res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (cancel) return NS_OK; // rules canceled the operation
|
|
|
|
if (!handled)
|
1999-11-11 02:42:11 +03:00
|
|
|
{
|
2000-01-10 13:13:58 +03:00
|
|
|
nsAutoString tag("blockquote");
|
|
|
|
res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!newNode) return NS_ERROR_NULL_POINTER;
|
1999-11-11 02:42:11 +03:00
|
|
|
|
2000-01-10 13:13:58 +03:00
|
|
|
// Try to set type=cite. Ignore it if this fails.
|
|
|
|
nsCOMPtr<nsIDOMElement> newElement (do_QueryInterface(newNode));
|
|
|
|
if (newElement)
|
|
|
|
{
|
|
|
|
nsAutoString type ("type");
|
|
|
|
nsAutoString cite ("cite");
|
|
|
|
newElement->SetAttribute(type, cite);
|
1999-11-18 22:43:14 +03:00
|
|
|
|
2000-01-10 13:13:58 +03:00
|
|
|
if (aCitation.Length() > 0)
|
|
|
|
newElement->SetAttribute(cite, aCitation);
|
1999-11-11 02:42:11 +03:00
|
|
|
|
2000-01-10 13:13:58 +03:00
|
|
|
// Set the selection inside the blockquote so aQuotedText will go there:
|
2000-01-28 04:17:23 +03:00
|
|
|
selection->Collapse(newNode, 0);
|
2000-01-10 13:13:58 +03:00
|
|
|
}
|
|
|
|
|
2000-03-21 02:13:25 +03:00
|
|
|
if (aInsertHTML)
|
|
|
|
res = InsertHTMLWithCharset(aQuotedText, aCharset);
|
|
|
|
|
|
|
|
else
|
|
|
|
res = InsertText(aQuotedText); // XXX ignore charset
|
|
|
|
|
2000-01-10 13:13:58 +03:00
|
|
|
if (aNodeInserted)
|
1999-11-24 23:48:59 +03:00
|
|
|
{
|
2000-01-10 13:13:58 +03:00
|
|
|
if (NS_SUCCEEDED(res))
|
|
|
|
{
|
|
|
|
*aNodeInserted = newNode;
|
|
|
|
NS_IF_ADDREF(*aNodeInserted);
|
|
|
|
}
|
1999-11-24 23:48:59 +03:00
|
|
|
}
|
1999-11-30 00:15:57 +03:00
|
|
|
|
2000-01-10 13:13:58 +03:00
|
|
|
// Set the selection to just after the inserted node:
|
|
|
|
if (NS_SUCCEEDED(res) && newNode)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> parent;
|
|
|
|
PRInt32 offset;
|
|
|
|
if (NS_SUCCEEDED(GetNodeLocation(newNode, &parent, &offset)) && parent)
|
|
|
|
selection->Collapse(parent, offset+1);
|
|
|
|
}
|
1999-11-30 00:15:57 +03:00
|
|
|
}
|
1999-11-11 02:42:11 +03:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP nsHTMLEditor::OutputToString(nsString& aOutputString,
|
|
|
|
const nsString& aFormatType,
|
|
|
|
PRUint32 aFlags)
|
1999-04-20 21:47:12 +04:00
|
|
|
{
|
1999-10-06 23:34:09 +04:00
|
|
|
PRBool cancel, handled;
|
1999-09-09 23:39:36 +04:00
|
|
|
nsString resultString;
|
|
|
|
nsTextRulesInfo ruleInfo(nsTextEditRules::kOutputText);
|
|
|
|
ruleInfo.outString = &resultString;
|
|
|
|
ruleInfo.outputFormat = &aFormatType;
|
1999-10-06 23:34:09 +04:00
|
|
|
nsresult rv = mRules->WillDoAction(nsnull, &ruleInfo, &cancel, &handled);
|
|
|
|
if (cancel || NS_FAILED(rv)) { return rv; }
|
|
|
|
if (handled)
|
1999-09-09 23:39:36 +04:00
|
|
|
{ // this case will get triggered by password fields
|
|
|
|
aOutputString = *(ruleInfo.outString);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // default processing
|
1999-09-14 01:39:51 +04:00
|
|
|
rv = NS_OK;
|
1999-09-09 03:32:04 +04:00
|
|
|
|
|
|
|
// special-case for empty document when requesting plain text,
|
|
|
|
// to account for the bogus text node
|
2000-03-26 15:39:08 +04:00
|
|
|
if (aFormatType.Equals("text/plain"))
|
1999-09-09 03:32:04 +04:00
|
|
|
{
|
|
|
|
PRBool docEmpty;
|
|
|
|
rv = GetDocumentIsEmpty(&docEmpty);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
if (docEmpty) {
|
|
|
|
aOutputString = "";
|
|
|
|
return NS_OK;
|
|
|
|
}
|
1999-09-30 00:23:07 +04:00
|
|
|
else if (mFlags & eEditorPlaintextMask)
|
|
|
|
aFlags |= nsIDocumentEncoder::OutputPreformatted;
|
1999-09-09 03:32:04 +04:00
|
|
|
}
|
|
|
|
|
1999-08-24 22:30:19 +04:00
|
|
|
nsCOMPtr<nsIDocumentEncoder> encoder;
|
1999-09-06 10:22:51 +04:00
|
|
|
char* progid = (char *)nsAllocator::Alloc(strlen(NS_DOC_ENCODER_PROGID_BASE) + aFormatType.Length() + 1);
|
1999-08-09 05:37:50 +04:00
|
|
|
if (! progid)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
strcpy(progid, NS_DOC_ENCODER_PROGID_BASE);
|
|
|
|
char* type = aFormatType.ToNewCString();
|
|
|
|
strcat(progid, type);
|
1999-09-06 10:22:51 +04:00
|
|
|
nsCRT::free(type);
|
1999-09-09 03:32:04 +04:00
|
|
|
rv = nsComponentManager::CreateInstance(progid,
|
1999-08-09 05:37:50 +04:00
|
|
|
nsnull,
|
2000-02-01 17:26:27 +03:00
|
|
|
NS_GET_IID(nsIDocumentEncoder),
|
1999-08-09 05:37:50 +04:00
|
|
|
getter_AddRefs(encoder));
|
|
|
|
|
1999-09-06 10:22:51 +04:00
|
|
|
nsCRT::free(progid);
|
1999-08-09 05:37:50 +04:00
|
|
|
if (NS_FAILED(rv))
|
1999-05-01 02:40:18 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
printf("Couldn't get progid %s\n", progid);
|
|
|
|
return rv;
|
1999-05-01 02:40:18 +04:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMDocument> domdoc;
|
|
|
|
rv = GetDocument(getter_AddRefs(domdoc));
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
|
|
|
nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
|
|
|
|
|
1999-08-26 01:42:47 +04:00
|
|
|
rv = encoder->Init(doc, aFormatType, aFlags);
|
1999-08-09 05:37:50 +04:00
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
|
|
|
|
1999-08-24 22:30:19 +04:00
|
|
|
// Set the selection, if appropriate:
|
|
|
|
if (aFlags & nsIDocumentEncoder::OutputSelectionOnly)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
1999-08-24 22:30:19 +04:00
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
|
|
|
rv = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_SUCCEEDED(rv) && selection)
|
|
|
|
encoder->SetSelection(selection);
|
|
|
|
}
|
1999-09-30 00:08:15 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
// Set the wrap column. If our wrap column is 0,
|
|
|
|
// i.e. wrap to body width, then don't set it, let the
|
|
|
|
// document encoder use its own default.
|
1999-08-20 02:11:58 +04:00
|
|
|
PRInt32 wrapColumn;
|
|
|
|
if (NS_SUCCEEDED(GetBodyWrapWidth(&wrapColumn)))
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
1999-08-20 02:11:58 +04:00
|
|
|
if (wrapColumn != 0)
|
|
|
|
{
|
|
|
|
PRUint32 wc;
|
|
|
|
if (wrapColumn < 0)
|
|
|
|
wc = 0;
|
|
|
|
else
|
|
|
|
wc = (PRUint32)wrapColumn;
|
|
|
|
if (wrapColumn > 0)
|
|
|
|
(void)encoder->SetWrapColumn(wc);
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
rv = encoder->EncodeToString(aOutputString);
|
1999-09-09 23:39:36 +04:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
return rv;
|
1999-04-20 21:47:12 +04:00
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
NS_IMETHODIMP nsHTMLEditor::OutputToStream(nsIOutputStream* aOutputStream,
|
|
|
|
const nsString& aFormatType,
|
|
|
|
const nsString* aCharset,
|
|
|
|
PRUint32 aFlags)
|
1999-04-20 21:47:12 +04:00
|
|
|
{
|
1999-09-09 03:32:04 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsresult rv;
|
1999-09-09 03:32:04 +04:00
|
|
|
|
|
|
|
// special-case for empty document when requesting plain text,
|
|
|
|
// to account for the bogus text node
|
2000-03-26 15:39:08 +04:00
|
|
|
if (aFormatType.Equals("text/plain"))
|
1999-09-09 03:32:04 +04:00
|
|
|
{
|
|
|
|
PRBool docEmpty;
|
|
|
|
rv = GetDocumentIsEmpty(&docEmpty);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
if (docEmpty)
|
|
|
|
return NS_OK; // output nothing
|
|
|
|
}
|
|
|
|
|
1999-08-24 22:30:19 +04:00
|
|
|
nsCOMPtr<nsIDocumentEncoder> encoder;
|
1999-09-06 10:22:51 +04:00
|
|
|
char* progid = (char *)nsAllocator::Alloc(strlen(NS_DOC_ENCODER_PROGID_BASE) + aFormatType.Length() + 1);
|
1999-08-09 05:37:50 +04:00
|
|
|
if (! progid)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
|
|
|
strcpy(progid, NS_DOC_ENCODER_PROGID_BASE);
|
|
|
|
char* type = aFormatType.ToNewCString();
|
|
|
|
strcat(progid, type);
|
1999-09-06 10:22:51 +04:00
|
|
|
nsCRT::free(type);
|
1999-08-09 05:37:50 +04:00
|
|
|
rv = nsComponentManager::CreateInstance(progid,
|
|
|
|
nsnull,
|
2000-02-01 17:26:27 +03:00
|
|
|
NS_GET_IID(nsIDocumentEncoder),
|
1999-08-09 05:37:50 +04:00
|
|
|
getter_AddRefs(encoder));
|
|
|
|
|
1999-09-06 10:22:51 +04:00
|
|
|
nsCRT::free(progid);
|
1999-08-09 05:37:50 +04:00
|
|
|
if (NS_FAILED(rv))
|
|
|
|
{
|
|
|
|
printf("Couldn't get progid %s\n", progid);
|
|
|
|
return rv;
|
|
|
|
}
|
1999-06-10 23:41:40 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMDocument> domdoc;
|
|
|
|
rv = GetDocument(getter_AddRefs(domdoc));
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
|
|
|
nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
|
1999-06-10 23:41:40 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
if (aCharset && aCharset->Length() != 0 && aCharset->Equals("null")==PR_FALSE)
|
|
|
|
encoder->SetCharset(*aCharset);
|
|
|
|
|
1999-08-26 01:42:47 +04:00
|
|
|
rv = encoder->Init(doc, aFormatType, aFlags);
|
1999-08-09 05:37:50 +04:00
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
|
|
|
|
1999-08-24 22:30:19 +04:00
|
|
|
// Set the selection, if appropriate:
|
|
|
|
if (aFlags & nsIDocumentEncoder::OutputSelectionOnly)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
1999-08-24 22:30:19 +04:00
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
1999-08-09 05:37:50 +04:00
|
|
|
rv = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_SUCCEEDED(rv) && selection)
|
|
|
|
encoder->SetSelection(selection);
|
|
|
|
}
|
1999-09-30 00:08:15 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
// Set the wrap column. If our wrap column is 0,
|
|
|
|
// i.e. wrap to body width, then don't set it, let the
|
|
|
|
// document encoder use its own default.
|
1999-08-24 22:30:19 +04:00
|
|
|
PRInt32 wrapColumn;
|
|
|
|
if (NS_SUCCEEDED(GetBodyWrapWidth(&wrapColumn)))
|
|
|
|
{
|
|
|
|
if (wrapColumn != 0)
|
1999-08-20 02:11:58 +04:00
|
|
|
{
|
1999-08-24 22:30:19 +04:00
|
|
|
PRUint32 wc;
|
|
|
|
if (wrapColumn < 0)
|
|
|
|
wc = 0;
|
|
|
|
else
|
|
|
|
wc = (PRUint32)wrapColumn;
|
|
|
|
if (wrapColumn > 0)
|
|
|
|
(void)encoder->SetWrapColumn(wc);
|
1999-08-20 02:11:58 +04:00
|
|
|
}
|
1999-08-24 22:30:19 +04:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
return encoder->EncodeToStream(aOutputStream);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (!outNumTests || !outNumTestsFailed)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
1999-09-30 00:08:15 +04:00
|
|
|
TextEditorTest *tester = new TextEditorTest();
|
|
|
|
if (!tester)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
tester->Run(this, outNumTests, outNumTestsFailed);
|
|
|
|
delete tester;
|
|
|
|
return NS_OK;
|
|
|
|
#else
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
1999-09-18 03:15:12 +04:00
|
|
|
#ifdef XP_MAC
|
|
|
|
#pragma mark -
|
2000-03-24 03:26:47 +03:00
|
|
|
#pragma mark nsIEditorIMESupport overrides
|
1999-09-18 03:15:12 +04:00
|
|
|
#pragma mark -
|
|
|
|
#endif
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::SetCompositionString(const nsString& aCompositionString, nsIPrivateTextRangeList* aTextRangeList,nsTextEventReply* aReply)
|
|
|
|
{
|
1999-10-27 00:04:47 +04:00
|
|
|
NS_ASSERTION(aTextRangeList, "null ptr");
|
|
|
|
if(nsnull == aTextRangeList)
|
2000-01-15 17:29:29 +03:00
|
|
|
return NS_ERROR_NULL_POINTER;
|
1999-09-18 03:15:12 +04:00
|
|
|
nsCOMPtr<nsICaret> caretP;
|
|
|
|
|
2000-02-26 02:50:17 +03:00
|
|
|
// workaround for windows ime bug 23558: we get every ime event twice.
|
|
|
|
// for escape keypress, this causes an empty string to be passed
|
|
|
|
// twice, which freaks out the editor. This is to detect and aviod that
|
|
|
|
// situation:
|
|
|
|
if (aCompositionString.IsEmpty() && !mIMETextNode)
|
|
|
|
{
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-09-18 03:15:12 +04:00
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
|
|
|
nsresult result = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(result)) return result;
|
1999-10-26 22:54:47 +04:00
|
|
|
|
1999-10-27 00:04:47 +04:00
|
|
|
mIMETextRangeList = aTextRangeList;
|
1999-10-26 22:54:47 +04:00
|
|
|
nsAutoPlaceHolderBatch batch(this, gIMETxnName);
|
|
|
|
|
|
|
|
result = InsertText(aCompositionString);
|
1999-10-27 00:04:47 +04:00
|
|
|
|
1999-09-18 03:15:12 +04:00
|
|
|
mIMEBufferLength = aCompositionString.Length();
|
|
|
|
|
|
|
|
if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
|
|
|
|
if (!ps) return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
ps->GetCaret(getter_AddRefs(caretP));
|
|
|
|
caretP->GetWindowRelativeCoordinates(aReply->mCursorPosition,aReply->mCursorIsCollapsed);
|
|
|
|
|
2000-02-26 02:50:17 +03:00
|
|
|
// second part of 23558 fix:
|
|
|
|
if (aCompositionString.IsEmpty())
|
|
|
|
{
|
|
|
|
mIMETextNode = nsnull;
|
|
|
|
}
|
|
|
|
|
1999-09-18 03:15:12 +04:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
#ifdef XP_MAC
|
|
|
|
#pragma mark -
|
2000-03-24 03:26:47 +03:00
|
|
|
#pragma mark StyleSheet utils
|
1999-08-09 05:37:50 +04:00
|
|
|
#pragma mark -
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::ReplaceStyleSheet(nsICSSStyleSheet *aNewSheet)
|
|
|
|
{
|
|
|
|
nsresult rv = NS_OK;
|
1999-04-22 18:45:48 +04:00
|
|
|
|
1999-09-30 00:08:15 +04:00
|
|
|
nsAutoEditBatch batchIt(this);
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
if (mLastStyleSheet)
|
1999-04-22 18:45:48 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
rv = RemoveStyleSheet(mLastStyleSheet);
|
1999-08-19 17:30:48 +04:00
|
|
|
//XXX: rv is ignored here, why?
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
rv = AddStyleSheet(aNewSheet);
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
1999-12-04 04:29:18 +03:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::StyleSheetLoaded(nsICSSStyleSheet*aSheet, PRBool aNotify)
|
|
|
|
{
|
|
|
|
ApplyStyleSheetToPresShellDocument(aSheet, this);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
/* static callback */
|
|
|
|
void nsHTMLEditor::ApplyStyleSheetToPresShellDocument(nsICSSStyleSheet* aSheet, void *aData)
|
|
|
|
{
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
|
|
|
|
nsHTMLEditor *editor = NS_STATIC_CAST(nsHTMLEditor*, aData);
|
|
|
|
if (editor)
|
|
|
|
{
|
|
|
|
rv = editor->ReplaceStyleSheet(aSheet);
|
|
|
|
}
|
1999-08-19 17:30:48 +04:00
|
|
|
// XXX: we lose the return value here. Set a flag in the editor?
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-12-07 11:30:19 +03:00
|
|
|
#ifdef XP_MAC
|
|
|
|
#pragma mark -
|
2000-03-24 03:26:47 +03:00
|
|
|
#pragma mark nsEditor overrides
|
1999-12-07 11:30:19 +03:00
|
|
|
#pragma mark -
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
/** All editor operations which alter the doc should be prefaced
|
|
|
|
* with a call to StartOperation, naming the action and direction */
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection)
|
|
|
|
{
|
2000-01-31 13:30:12 +03:00
|
|
|
if (! ((opID==kOpInsertText) || (opID==kOpInsertIMEText)) )
|
|
|
|
ClearInlineStylesCache();
|
1999-12-07 11:30:19 +03:00
|
|
|
if (mRules) return mRules->BeforeEdit(opID, 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
|
|
|
nsHTMLEditor::EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection)
|
1999-12-07 11:30:19 +03:00
|
|
|
{
|
2000-01-31 13:30:12 +03:00
|
|
|
if (! ((opID==kOpInsertText) || (opID==kOpInsertIMEText)) )
|
|
|
|
ClearInlineStylesCache();
|
2000-03-24 03:26:47 +03:00
|
|
|
if (mRules) return mRules->AfterEdit(opID, aDirection);
|
1999-12-07 11:30:19 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-01-10 13:13:58 +03:00
|
|
|
PRBool
|
|
|
|
nsHTMLEditor::CanContainTag(nsIDOMNode* aParent, const nsString &aTag)
|
|
|
|
{
|
|
|
|
// CNavDTD gives some unwanted results. We override them here.
|
|
|
|
// if parent is a list and tag is text, say "no".
|
2000-03-26 15:39:08 +04:00
|
|
|
if (IsListNode(aParent) && (aTag.Equals("__moz_text")))
|
2000-01-10 13:13:58 +03:00
|
|
|
return PR_FALSE;
|
|
|
|
// else fall thru
|
|
|
|
return nsEditor::CanContainTag(aParent, aTag);
|
|
|
|
}
|
|
|
|
|
1999-12-07 11:30:19 +03:00
|
|
|
|
2000-01-13 13:17:35 +03:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::SelectEntireDocument(nsIDOMSelection *aSelection)
|
|
|
|
{
|
|
|
|
nsresult res;
|
|
|
|
if (!aSelection || !mRules) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
|
|
|
|
// get body node
|
|
|
|
nsCOMPtr<nsIDOMElement>bodyElement;
|
|
|
|
res = GetBodyElement(getter_AddRefs(bodyElement));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
nsCOMPtr<nsIDOMNode>bodyNode = do_QueryInterface(bodyElement);
|
|
|
|
if (!bodyNode) return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
// is doc empty?
|
|
|
|
PRBool bDocIsEmpty;
|
|
|
|
res = mRules->DocumentIsEmpty(&bDocIsEmpty);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
if (bDocIsEmpty)
|
|
|
|
{
|
|
|
|
// if its empty dont select entire doc - that would select the bogus node
|
|
|
|
return aSelection->Collapse(bodyNode, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return nsEditor::SelectEntireDocument(aSelection);
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
#ifdef XP_MAC
|
|
|
|
#pragma mark -
|
2000-03-24 03:26:47 +03:00
|
|
|
#pragma mark Random methods
|
1999-08-09 05:37:50 +04:00
|
|
|
#pragma mark -
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsHTMLEditor::GetLayoutObject(nsIDOMNode *aNode, nsISupports **aLayoutObject)
|
|
|
|
{
|
|
|
|
nsresult result = NS_ERROR_FAILURE; // we return an error unless we get the index
|
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;
|
|
|
|
|
|
|
|
if ((nsnull!=aNode))
|
|
|
|
{ // get the content interface
|
|
|
|
nsCOMPtr<nsIContent> nodeAsContent( do_QueryInterface(aNode) );
|
|
|
|
if (nodeAsContent)
|
|
|
|
{ // get the frame from the content interface
|
|
|
|
//Note: frames are not ref counted, so don't use an nsCOMPtr
|
|
|
|
*aLayoutObject = nsnull;
|
|
|
|
result = ps->GetLayoutObjectFor(nodeAsContent, aLayoutObject);
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
}
|
1999-08-25 14:51:55 +04:00
|
|
|
else {
|
|
|
|
result = NS_ERROR_NULL_POINTER;
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// this will NOT find aAttribute unless aAttribute has a non-null value
|
|
|
|
// so singleton attributes like <Table border> will not be matched!
|
|
|
|
void nsHTMLEditor::IsTextPropertySetByContent(nsIDOMNode *aNode,
|
|
|
|
nsIAtom *aProperty,
|
|
|
|
const nsString *aAttribute,
|
|
|
|
const nsString *aValue,
|
|
|
|
PRBool &aIsSet,
|
|
|
|
nsIDOMNode **aStyleNode) const
|
|
|
|
{
|
|
|
|
nsresult result;
|
|
|
|
aIsSet = PR_FALSE; // must be initialized to false for code below to work
|
|
|
|
nsAutoString propName;
|
|
|
|
aProperty->ToString(propName);
|
|
|
|
nsCOMPtr<nsIDOMNode>parent;
|
|
|
|
result = aNode->GetParentNode(getter_AddRefs(parent));
|
1999-09-30 00:08:15 +04:00
|
|
|
if (NS_FAILED(result)) return;
|
1999-08-19 17:30:48 +04:00
|
|
|
while (parent)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMElement>element;
|
|
|
|
element = do_QueryInterface(parent);
|
|
|
|
if (element)
|
|
|
|
{
|
1999-09-15 03:40:16 +04:00
|
|
|
nsAutoString tag;
|
1999-08-09 05:37:50 +04:00
|
|
|
element->GetTagName(tag);
|
|
|
|
if (propName.EqualsIgnoreCase(tag))
|
1999-04-22 18:45:48 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
PRBool found = PR_FALSE;
|
|
|
|
if (aAttribute && 0!=aAttribute->Length())
|
|
|
|
{
|
|
|
|
nsAutoString value;
|
|
|
|
element->GetAttribute(*aAttribute, value);
|
|
|
|
if (0!=value.Length())
|
|
|
|
{
|
|
|
|
if (!aValue) {
|
|
|
|
found = PR_TRUE;
|
|
|
|
}
|
|
|
|
else if (aValue->EqualsIgnoreCase(value)) {
|
|
|
|
found = PR_TRUE;
|
|
|
|
}
|
|
|
|
else { // we found the prop with the attribute, but the value doesn't match
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
found = PR_TRUE;
|
|
|
|
}
|
2000-03-24 03:26:47 +03:00
|
|
|
if (found)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
|
|
|
aIsSet = PR_TRUE;
|
|
|
|
break;
|
|
|
|
}
|
1999-05-05 08:05:19 +04:00
|
|
|
}
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
nsCOMPtr<nsIDOMNode>temp;
|
|
|
|
result = parent->GetParentNode(getter_AddRefs(temp));
|
|
|
|
if (NS_SUCCEEDED(result) && temp) {
|
|
|
|
parent = do_QueryInterface(temp);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
parent = do_QueryInterface(nsnull);
|
|
|
|
}
|
1999-05-05 08:05:19 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
void nsHTMLEditor::IsTextStyleSet(nsIStyleContext *aSC,
|
|
|
|
nsIAtom *aProperty,
|
|
|
|
const nsString *aAttribute,
|
|
|
|
PRBool &aIsSet) const
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
aIsSet = PR_FALSE;
|
|
|
|
if (aSC && aProperty)
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
nsStyleFont* font = (nsStyleFont*)aSC->GetStyleData(eStyleStruct_Font);
|
|
|
|
if (nsIEditProperty::i==aProperty)
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
1999-08-09 05:37:50 +04:00
|
|
|
aIsSet = PRBool(font->mFont.style & NS_FONT_STYLE_ITALIC);
|
|
|
|
}
|
|
|
|
else if (nsIEditProperty::b==aProperty)
|
|
|
|
{ // XXX: check this logic with Peter
|
|
|
|
aIsSet = PRBool(font->mFont.weight > NS_FONT_WEIGHT_NORMAL);
|
1999-05-05 08:05:19 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
#ifdef XP_MAC
|
|
|
|
#pragma mark -
|
|
|
|
#endif
|
1999-08-09 05:37:50 +04:00
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
//================================================================
|
|
|
|
// HTML Editor methods
|
|
|
|
//
|
|
|
|
// Note: Table Editing methods are implemented in nsTableEditor.cpp
|
|
|
|
//
|
1999-08-09 05:37:50 +04:00
|
|
|
|
1999-08-19 17:30:48 +04:00
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
PRBool nsHTMLEditor::IsElementInBody(nsIDOMElement* aElement)
|
|
|
|
{
|
|
|
|
if ( aElement )
|
1999-08-24 12:56:51 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
nsIDOMElement* bodyElement = nsnull;
|
|
|
|
nsresult res = nsEditor::GetBodyElement(&bodyElement);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!bodyElement) return NS_ERROR_NULL_POINTER;
|
|
|
|
nsCOMPtr<nsIDOMNode> parent;
|
|
|
|
nsCOMPtr<nsIDOMNode> currentElement = do_QueryInterface(aElement);
|
|
|
|
if (currentElement)
|
|
|
|
{
|
|
|
|
do {
|
|
|
|
currentElement->GetParentNode(getter_AddRefs(parent));
|
|
|
|
if (parent)
|
|
|
|
{
|
|
|
|
if (parent == bodyElement)
|
|
|
|
return PR_TRUE;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
currentElement = parent;
|
|
|
|
}
|
|
|
|
} while(parent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return PR_FALSE;
|
1999-09-14 00:44:38 +04:00
|
|
|
}
|
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
PRBool
|
|
|
|
nsHTMLEditor::SetCaretInTableCell(nsIDOMElement* aElement)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
PRBool caretIsSet = PR_FALSE;
|
1999-08-09 05:37:50 +04:00
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
if (aElement && IsElementInBody(aElement))
|
1999-05-08 02:26:23 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
nsresult res = NS_OK;
|
|
|
|
nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
|
|
|
|
if (content)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
nsCOMPtr<nsIAtom> atom;
|
|
|
|
content->GetTag(*getter_AddRefs(atom));
|
|
|
|
if (atom.get() == nsIEditProperty::table ||
|
|
|
|
atom.get() == nsIEditProperty::tbody ||
|
|
|
|
atom.get() == nsIEditProperty::thead ||
|
|
|
|
atom.get() == nsIEditProperty::tfoot ||
|
|
|
|
atom.get() == nsIEditProperty::caption ||
|
|
|
|
atom.get() == nsIEditProperty::tr ||
|
|
|
|
atom.get() == nsIEditProperty::td )
|
1999-08-19 17:30:48 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aElement);
|
|
|
|
nsCOMPtr<nsIDOMNode> parent;
|
|
|
|
// This MUST succeed if IsElementInBody was TRUE
|
|
|
|
node->GetParentNode(getter_AddRefs(parent));
|
|
|
|
nsCOMPtr<nsIDOMNode>firstChild;
|
|
|
|
// Find deepest child
|
|
|
|
PRBool hasChild;
|
|
|
|
while (NS_SUCCEEDED(node->HasChildNodes(&hasChild)) && hasChild)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
if (NS_SUCCEEDED(node->GetFirstChild(getter_AddRefs(firstChild))))
|
|
|
|
{
|
|
|
|
parent = node;
|
|
|
|
node = firstChild;
|
1999-05-27 01:40:51 +04:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
2000-03-29 16:53:23 +04:00
|
|
|
# if 0
|
|
|
|
// I've ifdef'd this out because it isn't finished and I'm not sure what the intent is.
|
|
|
|
PRInt32 offset = 0;
|
|
|
|
nsCOMPtr<nsIDOMNode>lastChild;
|
|
|
|
res = parent->GetLastChild(getter_AddRefs(lastChild));
|
|
|
|
if (NS_SUCCEEDED(res) && lastChild && node != lastChild)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
if (node == lastChild)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
// Check if node is text and has more than just a  
|
|
|
|
nsCOMPtr<nsIDOMCharacterData>textNode = do_QueryInterface(node);
|
|
|
|
nsAutoString text;
|
|
|
|
PRUnichar nbspStr[2] = {nbsp, 0};
|
|
|
|
if (textNode && textNode->GetData(text))
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
// Set selection relative to the text node
|
|
|
|
parent = node;
|
|
|
|
PRInt32 len = text.Length();
|
|
|
|
if (len > 1 || text != nbspStr)
|
|
|
|
{
|
|
|
|
offset = len;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
}
|
2000-03-29 16:53:23 +04:00
|
|
|
} else {
|
|
|
|
// We have > 1 node, so set to end of content
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
1999-05-27 01:40:51 +04:00
|
|
|
}
|
2000-03-29 16:53:23 +04:00
|
|
|
#endif
|
|
|
|
// Set selection at beginning of deepest node
|
|
|
|
nsCOMPtr<nsIDOMSelection> selection;
|
|
|
|
res = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_SUCCEEDED(res) && selection && firstChild)
|
|
|
|
{
|
|
|
|
res = selection->Collapse(firstChild, 0);
|
|
|
|
if (NS_SUCCEEDED(res))
|
|
|
|
caretIsSet = PR_TRUE;
|
|
|
|
}
|
1999-05-27 01:40:51 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2000-03-29 16:53:23 +04:00
|
|
|
return caretIsSet;
|
|
|
|
}
|
1999-08-19 17:30:48 +04:00
|
|
|
|
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2000-03-29 16:53:23 +04:00
|
|
|
nsHTMLEditor::IsRootTag(nsString &aTag, PRBool &aIsTag)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
static char bodyTag[] = "body";
|
|
|
|
static char tdTag[] = "td";
|
|
|
|
static char thTag[] = "th";
|
|
|
|
static char captionTag[] = "caption";
|
|
|
|
if (aTag.EqualsIgnoreCase(bodyTag) ||
|
|
|
|
aTag.EqualsIgnoreCase(tdTag) ||
|
|
|
|
aTag.EqualsIgnoreCase(thTag) ||
|
|
|
|
aTag.EqualsIgnoreCase(captionTag) )
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
aIsTag = PR_TRUE;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
else {
|
2000-03-29 16:53:23 +04:00
|
|
|
aIsTag = PR_FALSE;
|
1999-07-14 19:24:33 +04:00
|
|
|
}
|
1999-09-23 08:01:10 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::IsSubordinateBlock(nsString &aTag, PRBool &aIsTag)
|
|
|
|
{
|
|
|
|
static char p[] = "p";
|
|
|
|
static char h1[] = "h1";
|
|
|
|
static char h2[] = "h2";
|
|
|
|
static char h3[] = "h3";
|
|
|
|
static char h4[] = "h4";
|
|
|
|
static char h5[] = "h5";
|
|
|
|
static char h6[] = "h6";
|
|
|
|
static char address[] = "address";
|
|
|
|
static char pre[] = "pre";
|
|
|
|
static char li[] = "li";
|
|
|
|
static char dt[] = "dt";
|
|
|
|
static char dd[] = "dd";
|
|
|
|
if (aTag.EqualsIgnoreCase(p) ||
|
|
|
|
aTag.EqualsIgnoreCase(h1) ||
|
|
|
|
aTag.EqualsIgnoreCase(h2) ||
|
|
|
|
aTag.EqualsIgnoreCase(h3) ||
|
|
|
|
aTag.EqualsIgnoreCase(h4) ||
|
|
|
|
aTag.EqualsIgnoreCase(h5) ||
|
|
|
|
aTag.EqualsIgnoreCase(h6) ||
|
|
|
|
aTag.EqualsIgnoreCase(address) ||
|
|
|
|
aTag.EqualsIgnoreCase(pre) ||
|
|
|
|
aTag.EqualsIgnoreCase(li) ||
|
|
|
|
aTag.EqualsIgnoreCase(dt) ||
|
|
|
|
aTag.EqualsIgnoreCase(dd) )
|
|
|
|
{
|
|
|
|
aIsTag = PR_TRUE;
|
1999-08-19 17:30:48 +04:00
|
|
|
}
|
|
|
|
else {
|
2000-03-29 16:53:23 +04:00
|
|
|
aIsTag = PR_FALSE;
|
1999-08-19 17:30:48 +04:00
|
|
|
}
|
2000-03-29 16:53:23 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
1999-08-19 17:30:48 +04:00
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// IsTable: true if node an html table
|
|
|
|
//
|
|
|
|
PRBool
|
|
|
|
nsHTMLEditor::IsTable(nsIDOMNode *node)
|
|
|
|
{
|
|
|
|
NS_PRECONDITION(node, "null node passed to nsHTMLEditor::IsTable");
|
|
|
|
nsAutoString tag;
|
|
|
|
nsEditor::GetTagString(node,tag);
|
|
|
|
if (tag.Equals("table"))
|
1999-05-27 01:40:51 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
return PR_TRUE;
|
1999-08-19 17:30:48 +04:00
|
|
|
}
|
2000-03-29 16:53:23 +04:00
|
|
|
return PR_FALSE;
|
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// IsTableCell: true if node an html td
|
|
|
|
//
|
|
|
|
PRBool
|
|
|
|
nsHTMLEditor::IsTableCell(nsIDOMNode *node)
|
|
|
|
{
|
|
|
|
NS_PRECONDITION(node, "null node passed to nsHTMLEditor::IsTableCell");
|
1999-08-19 17:30:48 +04:00
|
|
|
nsAutoString tag;
|
2000-03-29 16:53:23 +04:00
|
|
|
nsEditor::GetTagString(node,tag);
|
|
|
|
if (tag.Equals("td") || tag.Equals("th"))
|
1999-08-19 17:30:48 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
return PR_TRUE;
|
1999-08-19 17:30:48 +04:00
|
|
|
}
|
2000-03-29 16:53:23 +04:00
|
|
|
return PR_FALSE;
|
|
|
|
}
|
1999-08-19 17:30:48 +04:00
|
|
|
|
1999-08-09 05:37:50 +04:00
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// IsTableElement: true if node an html table, td, tr, ...
|
|
|
|
//
|
|
|
|
PRBool
|
|
|
|
nsHTMLEditor::IsTableElement(nsIDOMNode *node)
|
|
|
|
{
|
|
|
|
NS_PRECONDITION(node, "null node passed to nsHTMLEditor::IsTableElement");
|
|
|
|
nsAutoString tagName;
|
|
|
|
nsEditor::GetTagString(node,tagName);
|
|
|
|
if (tagName.Equals("table") || tagName.Equals("tr") ||
|
|
|
|
tagName.Equals("td") || tagName.Equals("th") ||
|
|
|
|
tagName.Equals("thead") || tagName.Equals("tfoot") ||
|
|
|
|
tagName.Equals("tbody") || tagName.Equals("caption"))
|
|
|
|
{
|
|
|
|
return PR_TRUE;
|
1999-05-08 02:26:23 +04:00
|
|
|
}
|
2000-03-29 16:53:23 +04:00
|
|
|
return PR_FALSE;
|
1999-05-08 02:26:23 +04:00
|
|
|
}
|
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// GetEnclosingTable: find ancestor who is a table, if any
|
|
|
|
//
|
|
|
|
nsCOMPtr<nsIDOMNode>
|
|
|
|
nsHTMLEditor::GetEnclosingTable(nsIDOMNode *aNode)
|
|
|
|
{
|
|
|
|
NS_PRECONDITION(aNode, "null node passed to nsHTMLEditor::GetEnclosingTable");
|
|
|
|
nsCOMPtr<nsIDOMNode> tbl, tmp, node = aNode;
|
|
|
|
|
|
|
|
while (!tbl)
|
1999-07-19 23:06:39 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
tmp = GetBlockNodeParent(node);
|
|
|
|
if (!tmp) break;
|
|
|
|
if (IsTable(tmp)) tbl = tmp;
|
|
|
|
node = tmp;
|
1999-07-19 23:06:39 +04:00
|
|
|
}
|
2000-03-29 16:53:23 +04:00
|
|
|
return tbl;
|
1999-07-19 23:06:39 +04:00
|
|
|
}
|
1999-05-17 16:22:31 +04:00
|
|
|
|
1999-05-05 08:05:19 +04:00
|
|
|
NS_IMETHODIMP
|
2000-03-29 16:53:23 +04:00
|
|
|
nsHTMLEditor::DeleteSelectionAndPrepareToCreateNode(nsCOMPtr<nsIDOMNode> &parentSelectedNode, PRInt32& offsetOfNewNode)
|
|
|
|
{
|
|
|
|
nsresult result=NS_ERROR_NOT_INITIALIZED;
|
|
|
|
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
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
PRBool collapsed;
|
|
|
|
result = selection->GetIsCollapsed(&collapsed);
|
|
|
|
if (NS_SUCCEEDED(result) && !collapsed)
|
1999-05-05 08:05:19 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
result = DeleteSelection(nsIEditor::eNone);
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
return result;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
2000-03-29 16:53:23 +04:00
|
|
|
// get the new selection
|
|
|
|
result = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
return result;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
2000-03-29 16:53:23 +04:00
|
|
|
#ifdef NS_DEBUG
|
|
|
|
nsCOMPtr<nsIDOMNode>testSelectedNode;
|
|
|
|
nsresult debugResult = selection->GetAnchorNode(getter_AddRefs(testSelectedNode));
|
|
|
|
// no selection is ok.
|
|
|
|
// if there is a selection, it must be collapsed
|
|
|
|
if (testSelectedNode)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
PRBool testCollapsed;
|
|
|
|
debugResult = selection->GetIsCollapsed(&testCollapsed);
|
|
|
|
NS_ASSERTION((NS_SUCCEEDED(result)), "couldn't get a selection after deletion");
|
|
|
|
NS_ASSERTION(testCollapsed, "selection not reset after deletion");
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
2000-03-29 16:53:23 +04:00
|
|
|
#endif
|
1999-05-05 08:05:19 +04:00
|
|
|
}
|
2000-03-29 16:53:23 +04:00
|
|
|
// split the selected node
|
|
|
|
PRInt32 offsetOfSelectedNode;
|
|
|
|
result = selection->GetAnchorNode(getter_AddRefs(parentSelectedNode));
|
|
|
|
if (NS_SUCCEEDED(result) && NS_SUCCEEDED(selection->GetAnchorOffset(&offsetOfSelectedNode)) && parentSelectedNode)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> selectedNode;
|
|
|
|
PRUint32 selectedNodeContentCount=0;
|
|
|
|
nsCOMPtr<nsIDOMCharacterData>selectedParentNodeAsText;
|
|
|
|
selectedParentNodeAsText = do_QueryInterface(parentSelectedNode);
|
|
|
|
|
|
|
|
offsetOfNewNode = offsetOfSelectedNode;
|
|
|
|
|
|
|
|
/* if the selection is a text node, split the text node if necesary
|
|
|
|
and compute where to put the new node
|
|
|
|
*/
|
|
|
|
if (selectedParentNodeAsText)
|
|
|
|
{
|
|
|
|
PRInt32 indexOfTextNodeInParent;
|
|
|
|
selectedNode = do_QueryInterface(parentSelectedNode);
|
|
|
|
selectedNode->GetParentNode(getter_AddRefs(parentSelectedNode));
|
|
|
|
selectedParentNodeAsText->GetLength(&selectedNodeContentCount);
|
|
|
|
GetChildOffset(selectedNode, parentSelectedNode, indexOfTextNodeInParent);
|
|
|
|
|
|
|
|
if ((offsetOfSelectedNode!=0) && (((PRUint32)offsetOfSelectedNode)!=selectedNodeContentCount))
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> newSiblingNode;
|
|
|
|
result = SplitNode(selectedNode, offsetOfSelectedNode, getter_AddRefs(newSiblingNode));
|
|
|
|
// now get the node's offset in it's parent, and insert the new tag there
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
result = GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode);
|
1999-08-19 17:30:48 +04:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
1999-08-19 17:30:48 +04:00
|
|
|
else
|
2000-03-29 16:53:23 +04:00
|
|
|
{ // determine where to insert the new node
|
|
|
|
if (0==offsetOfSelectedNode) {
|
|
|
|
offsetOfNewNode = indexOfTextNodeInParent; // insert new node as previous sibling to selection parent
|
|
|
|
}
|
|
|
|
else { // insert new node as last child
|
|
|
|
GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode);
|
|
|
|
offsetOfNewNode++; // offsets are 0-based, and we need the index of the new node
|
|
|
|
}
|
1999-08-19 17:30:48 +04:00
|
|
|
}
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
2000-03-29 16:53:23 +04:00
|
|
|
|
|
|
|
// I dont know what is up with this, but there is no reason to split
|
|
|
|
// any node we happen to be inserting into. The code below (ifdef'd out)
|
|
|
|
// breaks InsertBreak().
|
|
|
|
|
|
|
|
|
|
|
|
#if 0
|
1999-08-19 17:30:48 +04:00
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
/* if the selection is not a text node, split the parent node if necesary
|
|
|
|
and compute where to put the new node
|
|
|
|
*/
|
|
|
|
else
|
|
|
|
{ // it's an interior node
|
|
|
|
nsCOMPtr<nsIDOMNodeList>parentChildList;
|
|
|
|
parentSelectedNode->GetChildNodes(getter_AddRefs(parentChildList));
|
|
|
|
if ((NS_SUCCEEDED(result)) && parentChildList)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
result = parentChildList->Item(offsetOfSelectedNode, getter_AddRefs(selectedNode));
|
|
|
|
if ((NS_SUCCEEDED(result)) && selectedNode)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
nsCOMPtr<nsIDOMCharacterData>selectedNodeAsText;
|
|
|
|
selectedNodeAsText = do_QueryInterface(selectedNode);
|
|
|
|
nsCOMPtr<nsIDOMNodeList>childList;
|
|
|
|
//CM: I added "result ="
|
|
|
|
result = selectedNode->GetChildNodes(getter_AddRefs(childList));
|
|
|
|
if (NS_SUCCEEDED(result))
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
if (childList)
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
childList->GetLength(&selectedNodeContentCount);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// This is the case where the collapsed selection offset
|
|
|
|
// points to an inline node with no children
|
|
|
|
// This must also be where the new node should be inserted
|
|
|
|
// and there is no splitting necessary
|
|
|
|
offsetOfNewNode = offsetOfSelectedNode;
|
|
|
|
return NS_OK;
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
}
|
2000-03-29 16:53:23 +04:00
|
|
|
else
|
1999-08-09 05:37:50 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
if ((offsetOfSelectedNode!=0) && (((PRUint32)offsetOfSelectedNode)!=selectedNodeContentCount))
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> newSiblingNode;
|
|
|
|
result = SplitNode(selectedNode, offsetOfSelectedNode, getter_AddRefs(newSiblingNode));
|
|
|
|
// now get the node's offset in it's parent, and insert the new tag there
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
result = GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // determine where to insert the new node
|
|
|
|
if (0==offsetOfSelectedNode) {
|
|
|
|
offsetOfNewNode = 0; // insert new node as first child
|
|
|
|
}
|
|
|
|
else { // insert new node as last child
|
|
|
|
GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode);
|
|
|
|
offsetOfNewNode++; // offsets are 0-based, and we need the index of the new node
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2000-03-29 16:53:23 +04:00
|
|
|
#endif
|
1999-08-19 17:30:48 +04:00
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
// Here's where the new node was inserted
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
2000-03-29 16:53:23 +04:00
|
|
|
else {
|
|
|
|
printf("InsertBreak into an empty document is not yet supported\n");
|
1999-08-09 05:37:50 +04:00
|
|
|
}
|
|
|
|
return result;
|
1999-06-15 00:02:46 +04:00
|
|
|
}
|
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
|
|
|
|
#ifdef XP_MAC
|
|
|
|
#pragma mark -
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void nsHTMLEditor::CacheInlineStyles(nsIDOMNode *aNode)
|
|
|
|
{
|
|
|
|
if (!aNode) return;
|
|
|
|
nsCOMPtr<nsIDOMNode> resultNode;
|
|
|
|
mCachedNode = do_QueryInterface(aNode);
|
|
|
|
IsTextPropertySetByContent(aNode, mBoldAtom, 0, 0, mCachedBoldStyle, getter_AddRefs(resultNode));
|
|
|
|
IsTextPropertySetByContent(aNode, mItalicAtom, 0, 0, mCachedItalicStyle, getter_AddRefs(resultNode));
|
|
|
|
IsTextPropertySetByContent(aNode, mUnderlineAtom, 0, 0, mCachedUnderlineStyle, getter_AddRefs(resultNode));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void nsHTMLEditor::ClearInlineStylesCache()
|
|
|
|
{
|
|
|
|
mCachedNode = nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef PRE_NODE_IN_BODY
|
|
|
|
nsCOMPtr<nsIDOMElement> nsHTMLEditor::FindPreElement()
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMDocument> domdoc;
|
|
|
|
nsEditor::GetDocument(getter_AddRefs(domdoc));
|
|
|
|
if (!domdoc)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDocument> doc (do_QueryInterface(domdoc));
|
|
|
|
if (!doc)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
nsIContent* rootContent = doc->GetRootContent();
|
|
|
|
if (!rootContent)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNode> rootNode (do_QueryInterface(rootContent));
|
|
|
|
if (!rootNode)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
nsString prestr ("PRE"); // GetFirstNodeOfType requires capitals
|
|
|
|
nsCOMPtr<nsIDOMNode> preNode;
|
|
|
|
if (!NS_SUCCEEDED(nsEditor::GetFirstNodeOfType(rootNode, prestr,
|
|
|
|
getter_AddRefs(preNode))))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return do_QueryInterface(preNode);
|
|
|
|
}
|
|
|
|
#endif /* PRE_NODE_IN_BODY */
|
|
|
|
|
|
|
|
void nsHTMLEditor::HandleEventListenerError()
|
|
|
|
{
|
|
|
|
if (gNoisy) { printf("failed to add event listener\n"); }
|
|
|
|
// null out the nsCOMPtrs
|
|
|
|
mKeyListenerP = nsnull;
|
|
|
|
mMouseListenerP = nsnull;
|
|
|
|
mTextListenerP = nsnull;
|
|
|
|
mDragListenerP = nsnull;
|
|
|
|
mCompositionListenerP = nsnull;
|
|
|
|
mFocusListenerP = nsnull;
|
|
|
|
}
|
|
|
|
|
1999-08-19 19:15:41 +04:00
|
|
|
/* this method scans the selection for adjacent text nodes
|
|
|
|
* and collapses them into a single text node.
|
|
|
|
* "adjacent" means literally adjacent siblings of the same parent.
|
|
|
|
* Uses nsEditor::JoinNodes so action is undoable.
|
|
|
|
* Should be called within the context of a batch transaction.
|
1999-08-19 17:30:48 +04:00
|
|
|
*/
|
|
|
|
NS_IMETHODIMP
|
2000-03-29 16:53:23 +04:00
|
|
|
nsHTMLEditor::CollapseAdjacentTextNodes(nsIDOMRange *aInRange)
|
1999-08-19 17:30:48 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
if (!aInRange) return NS_ERROR_NULL_POINTER;
|
|
|
|
nsAutoTxnsConserveSelection dontSpazMySelection(this);
|
1999-09-30 00:08:15 +04:00
|
|
|
nsVoidArray textNodes; // we can't actually do anything during iteration, so store the text nodes in an array
|
1999-08-19 17:30:48 +04:00
|
|
|
// don't bother ref counting them because we know we can hold them for the
|
|
|
|
// lifetime of this method
|
|
|
|
|
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
// build a list of editable text nodes
|
|
|
|
nsCOMPtr<nsIContentIterator> iter;
|
|
|
|
nsresult result = nsComponentManager::CreateInstance(kSubtreeIteratorCID, nsnull,
|
|
|
|
NS_GET_IID(nsIContentIterator),
|
|
|
|
getter_AddRefs(iter));
|
1999-08-19 17:30:48 +04:00
|
|
|
if (NS_FAILED(result)) return result;
|
2000-03-29 16:53:23 +04:00
|
|
|
if (!iter) return NS_ERROR_NULL_POINTER;
|
1999-08-19 17:30:48 +04:00
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
iter->Init(aInRange);
|
|
|
|
nsCOMPtr<nsIContent> content;
|
|
|
|
result = iter->CurrentNode(getter_AddRefs(content));
|
|
|
|
while (NS_ENUMERATOR_FALSE == iter->IsDone())
|
1999-08-19 17:30:48 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
nsCOMPtr<nsIDOMCharacterData> text = do_QueryInterface(content);
|
|
|
|
nsCOMPtr<nsIDOMNode> node = do_QueryInterface(content);
|
|
|
|
if (text && node && IsEditable(node))
|
1999-08-19 17:30:48 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
textNodes.AppendElement((void*)(node.get()));
|
1999-08-19 17:30:48 +04:00
|
|
|
}
|
2000-03-29 16:53:23 +04:00
|
|
|
iter->Next();
|
|
|
|
iter->CurrentNode(getter_AddRefs(content));
|
1999-08-19 17:30:48 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// now that I have a list of text nodes, collapse adjacent text nodes
|
2000-03-29 16:53:23 +04:00
|
|
|
// NOTE: assumption that JoinNodes keeps the righthand node
|
|
|
|
nsIDOMNode *leftTextNode = (nsIDOMNode *)(textNodes.ElementAt(0));
|
|
|
|
nsIDOMNode *rightTextNode = (nsIDOMNode *)(textNodes.ElementAt(1));
|
1999-08-19 17:30:48 +04:00
|
|
|
|
2000-03-29 16:53:23 +04:00
|
|
|
while (leftTextNode && rightTextNode)
|
|
|
|
{
|
1999-09-30 00:08:15 +04:00
|
|
|
// get the prev sibling of the right node, and see if it's leftTextNode
|
2000-03-29 16:53:23 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> prevSibOfRightNode;
|
|
|
|
result = GetPriorHTMLSibling(rightTextNode, &prevSibOfRightNode);
|
1999-08-19 17:30:48 +04:00
|
|
|
if (NS_FAILED(result)) return result;
|
2000-03-29 16:53:23 +04:00
|
|
|
if (prevSibOfRightNode && (prevSibOfRightNode.get() == leftTextNode))
|
1999-08-19 17:30:48 +04:00
|
|
|
{
|
2000-03-29 16:53:23 +04:00
|
|
|
nsCOMPtr<nsIDOMNode> parent;
|
|
|
|
result = rightTextNode->GetParentNode(getter_AddRefs(parent));
|
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
if (!parent) return NS_ERROR_NULL_POINTER;
|
|
|
|
result = JoinNodes(leftTextNode, rightTextNode, parent);
|
1999-08-19 17:30:48 +04:00
|
|
|
if (NS_FAILED(result)) return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
textNodes.RemoveElementAt(0); // remove the leftmost text node from the list
|
2000-03-29 16:53:23 +04:00
|
|
|
leftTextNode = (nsIDOMNode *)(textNodes.ElementAt(0));
|
|
|
|
rightTextNode = (nsIDOMNode *)(textNodes.ElementAt(1));
|
1999-08-19 17:30:48 +04:00
|
|
|
}
|
|
|
|
|
1999-09-30 00:08:15 +04:00
|
|
|
return result;
|
1999-08-19 17:30:48 +04:00
|
|
|
}
|
1999-09-10 22:54:13 +04:00
|
|
|
|
2000-01-26 17:57:43 +03:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::GetNextElementByTagName(nsIDOMElement *aCurrentElement,
|
|
|
|
const nsString *aTagName,
|
|
|
|
nsIDOMElement **aReturn)
|
|
|
|
{
|
|
|
|
nsresult res = NS_OK;
|
|
|
|
if (!aCurrentElement || !aTagName || !aReturn)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
nsIAtom *tagAtom = NS_NewAtom(*aTagName);
|
|
|
|
if (!tagAtom) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
if (tagAtom==nsIEditProperty::th)
|
|
|
|
tagAtom=nsIEditProperty::td;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNode> currentNode = do_QueryInterface(aCurrentElement);
|
|
|
|
if (!currentNode)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
*aReturn = nsnull;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNode> nextNode;
|
|
|
|
PRBool done = PR_FALSE;
|
|
|
|
|
|
|
|
do {
|
|
|
|
res = GetNextNode(currentNode, PR_TRUE, getter_AddRefs(nextNode));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!nextNode) break;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIAtom> atom = GetTag(currentNode);
|
|
|
|
|
2000-01-26 19:01:49 +03:00
|
|
|
if (tagAtom==atom.get())
|
2000-01-26 17:57:43 +03:00
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMElement> element = do_QueryInterface(currentNode);
|
|
|
|
if (!element) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
*aReturn = element;
|
|
|
|
NS_ADDREF(*aReturn);
|
|
|
|
done = PR_TRUE;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
currentNode = nextNode;
|
|
|
|
} while (!done);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
2000-02-08 04:11:13 +03:00
|
|
|
|
2000-02-10 08:14:52 +03:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLEditor::SetSelectionAtDocumentStart(nsIDOMSelection *aSelection)
|
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
nsCOMPtr<nsIDOMElement> bodyElement;
|
|
|
|
nsresult res = GetBodyElement(getter_AddRefs(bodyElement));
|
|
|
|
if (NS_SUCCEEDED(res))
|
2000-02-10 08:14:52 +03:00
|
|
|
{
|
|
|
|
if (!bodyElement) return NS_ERROR_NULL_POINTER;
|
|
|
|
res = aSelection->Collapse(bodyElement,0);
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2000-02-08 15:53:34 +03:00
|
|
|
#ifdef XP_MAC
|
|
|
|
#pragma mark -
|
|
|
|
#endif
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange)
|
|
|
|
{
|
|
|
|
// Can only change font size by + or - 1
|
|
|
|
if ( !( (aSizeChange==1) || (aSizeChange==-1) ) )
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
|
|
|
|
ForceCompositionEnd();
|
|
|
|
|
|
|
|
// Get the selection
|
|
|
|
nsCOMPtr<nsIDOMSelection>selection;
|
|
|
|
nsresult res = GetSelection(getter_AddRefs(selection));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!selection) return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
// Is the selection collapsed?
|
|
|
|
PRBool bCollapsed;
|
|
|
|
res = selection->GetIsCollapsed(&bCollapsed);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
// if it's collapsed dont do anything.
|
|
|
|
// MOOSE: We should probably have typing state for this like
|
|
|
|
// we do for other things.
|
|
|
|
if (bCollapsed)
|
|
|
|
{
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
// wrap with txn batching, rules sniffing, and selection preservation code
|
|
|
|
nsAutoEditBatch batchIt(this);
|
|
|
|
nsAutoRules beginRulesSniffing(this, kOpSetTextProperty, nsIEditor::eNext);
|
|
|
|
nsAutoSelectionReset selectionResetter(selection, this);
|
|
|
|
|
2000-02-08 15:53:34 +03:00
|
|
|
// get selection range enumerator
|
|
|
|
nsCOMPtr<nsIEnumerator> enumerator;
|
|
|
|
res = selection->GetEnumerator(getter_AddRefs(enumerator));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!enumerator) return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
// loop thru the ranges in the selection
|
|
|
|
enumerator->First();
|
|
|
|
nsCOMPtr<nsISupports> currentItem;
|
|
|
|
while ((NS_ENUMERATOR_FALSE == enumerator->IsDone()))
|
|
|
|
{
|
|
|
|
res = enumerator->CurrentItem(getter_AddRefs(currentItem));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!currentItem) return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
|
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
// adjust range to include any ancestors who's children are entirely selected
|
|
|
|
res = PromoteInlineRange(range);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
2000-02-08 15:53:34 +03:00
|
|
|
// check for easy case: both range endpoints in same text node
|
|
|
|
nsCOMPtr<nsIDOMNode> startNode, endNode;
|
|
|
|
res = range->GetStartParent(getter_AddRefs(startNode));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
res = range->GetEndParent(getter_AddRefs(endNode));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if ((startNode == endNode) && IsTextNode(startNode))
|
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
// MOOSE: workaround for selection bug:
|
|
|
|
selection->ClearSelection();
|
|
|
|
|
2000-02-08 15:53:34 +03:00
|
|
|
PRInt32 startOffset, endOffset;
|
|
|
|
range->GetStartOffset(&startOffset);
|
|
|
|
range->GetEndOffset(&endOffset);
|
|
|
|
nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(startNode);
|
|
|
|
res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, startOffset, endOffset);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// not the easy case. range not contained in single text node.
|
|
|
|
// there are up to three phases here. There are all the nodes
|
|
|
|
// reported by the subtree iterator to be processed. And there
|
|
|
|
// are potentially a starting textnode and an ending textnode
|
|
|
|
// which are only partially contained by the range.
|
|
|
|
|
|
|
|
// lets handle the nodes reported by the iterator. These nodes
|
|
|
|
// are entirely contained in the selection range. We build up
|
|
|
|
// a list of them (since doing operations on the document during
|
|
|
|
// iteration would perturb the iterator).
|
|
|
|
|
|
|
|
nsCOMPtr<nsIContentIterator> iter;
|
|
|
|
res = nsComponentManager::CreateInstance(kSubtreeIteratorCID, nsnull,
|
|
|
|
NS_GET_IID(nsIContentIterator),
|
|
|
|
getter_AddRefs(iter));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!iter) return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
nsCOMPtr<nsISupportsArray> arrayOfNodes;
|
|
|
|
nsCOMPtr<nsIContent> content;
|
|
|
|
nsCOMPtr<nsIDOMNode> node;
|
|
|
|
nsCOMPtr<nsISupports> isupports;
|
|
|
|
|
|
|
|
// make a array
|
|
|
|
res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
// iterate range and build up array
|
|
|
|
iter->Init(range);
|
|
|
|
while (NS_ENUMERATOR_FALSE == iter->IsDone())
|
|
|
|
{
|
|
|
|
res = iter->CurrentNode(getter_AddRefs(content));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
node = do_QueryInterface(content);
|
|
|
|
if (!node) return NS_ERROR_FAILURE;
|
2000-03-24 03:26:47 +03:00
|
|
|
if (IsEditable(node))
|
|
|
|
{
|
|
|
|
isupports = do_QueryInterface(node);
|
|
|
|
arrayOfNodes->AppendElement(isupports);
|
|
|
|
}
|
2000-02-08 15:53:34 +03:00
|
|
|
res = iter->Next();
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
}
|
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
// MOOSE: workaround for selection bug:
|
|
|
|
selection->ClearSelection();
|
|
|
|
|
2000-02-08 15:53:34 +03:00
|
|
|
// now that we have the list, do the font size change on each node
|
|
|
|
PRUint32 listCount;
|
2000-02-15 18:54:05 +03:00
|
|
|
PRUint32 j;
|
2000-02-08 15:53:34 +03:00
|
|
|
arrayOfNodes->Count(&listCount);
|
|
|
|
for (j = 0; j < listCount; j++)
|
|
|
|
{
|
|
|
|
isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0));
|
|
|
|
node = do_QueryInterface(isupports);
|
|
|
|
res = RelativeFontChangeOnNode(aSizeChange, node);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
arrayOfNodes->RemoveElementAt(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// now check the start and end parents of the range to see if they need to
|
|
|
|
// be seperately handled (they do if they are text nodes, due to how the
|
|
|
|
// subtree iterator works - it will not have reported them).
|
2000-03-24 03:26:47 +03:00
|
|
|
if (IsTextNode(startNode) && IsEditable(startNode))
|
2000-02-08 15:53:34 +03:00
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(startNode);
|
|
|
|
PRInt32 startOffset;
|
|
|
|
PRUint32 textLen;
|
|
|
|
range->GetStartOffset(&startOffset);
|
|
|
|
nodeAsText->GetLength(&textLen);
|
|
|
|
res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, startOffset, textLen);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
}
|
2000-03-24 03:26:47 +03:00
|
|
|
if (IsTextNode(endNode) && IsEditable(endNode))
|
2000-02-08 15:53:34 +03:00
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(endNode);
|
|
|
|
PRInt32 endOffset;
|
|
|
|
range->GetEndOffset(&endOffset);
|
|
|
|
res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, 0, endOffset);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
enumerator->Next();
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsHTMLEditor::RelativeFontChangeOnTextNode( PRInt32 aSizeChange,
|
|
|
|
nsIDOMCharacterData *aTextNode,
|
|
|
|
PRInt32 aStartOffset,
|
|
|
|
PRInt32 aEndOffset)
|
|
|
|
{
|
|
|
|
// Can only change font size by + or - 1
|
|
|
|
if ( !( (aSizeChange==1) || (aSizeChange==-1) ) )
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
if (!aTextNode) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
// dont need to do anything if no characters actually selected
|
|
|
|
if (aStartOffset == aEndOffset) return NS_OK;
|
|
|
|
|
|
|
|
nsresult res = NS_OK;
|
|
|
|
nsCOMPtr<nsIDOMNode> tmp, node = do_QueryInterface(aTextNode);
|
|
|
|
|
|
|
|
// do we need to split the text node?
|
|
|
|
PRUint32 textLen;
|
|
|
|
aTextNode->GetLength(&textLen);
|
|
|
|
|
2000-02-15 18:54:05 +03:00
|
|
|
if ( (PRUint32)aEndOffset != textLen )
|
2000-02-08 15:53:34 +03:00
|
|
|
{
|
|
|
|
// we need to split off back of text node
|
|
|
|
res = SplitNode(node, aEndOffset, getter_AddRefs(tmp));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
node = tmp; // remember left node
|
|
|
|
}
|
|
|
|
if ( aStartOffset )
|
|
|
|
{
|
|
|
|
// we need to split off front of text node
|
|
|
|
res = SplitNode(node, aStartOffset, getter_AddRefs(tmp));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
// reparent the node inside font node with appropriate relative size
|
|
|
|
nsAutoString tag;
|
|
|
|
if (aSizeChange == 1) tag = "big";
|
|
|
|
else tag = "small";
|
|
|
|
res = InsertContainerAbove(node, &tmp, tag);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsHTMLEditor::RelativeFontChangeOnNode( PRInt32 aSizeChange,
|
|
|
|
nsIDOMNode *aNode)
|
|
|
|
{
|
|
|
|
// Can only change font size by + or - 1
|
|
|
|
if ( !( (aSizeChange==1) || (aSizeChange==-1) ) )
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
if (!aNode) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
nsresult res = NS_OK;
|
|
|
|
nsCOMPtr<nsIDOMNode> tmp;
|
|
|
|
nsAutoString tag;
|
|
|
|
if (aSizeChange == 1) tag = "big";
|
|
|
|
else tag = "small";
|
|
|
|
|
|
|
|
// is this node a text node?
|
|
|
|
if (IsTextNode(aNode))
|
|
|
|
{
|
|
|
|
res = InsertContainerAbove(aNode, &tmp, tag);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
// is it the opposite of what we want?
|
|
|
|
if ( ((aSizeChange == 1) && nsHTMLEditUtils::IsSmall(aNode)) ||
|
|
|
|
((aSizeChange == -1) && nsHTMLEditUtils::IsBig(aNode)) )
|
|
|
|
{
|
|
|
|
// in that case, just remove this node and pull up the children
|
|
|
|
res = RemoveContainer(aNode);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
// can it be put inside a "big" or "small"?
|
|
|
|
if (TagCanContain(tag, aNode))
|
|
|
|
{
|
|
|
|
// ok, chuck it in.
|
|
|
|
res = InsertContainerAbove(aNode, &tmp, tag);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
// none of the above? then cycle through the children.
|
|
|
|
// MOOSE: we should group the children together if possible
|
|
|
|
// into a single "big" or "small". For the moment they are
|
|
|
|
// each getting their own.
|
|
|
|
nsCOMPtr<nsIDOMNodeList> childNodes;
|
|
|
|
res = aNode->GetChildNodes(getter_AddRefs(childNodes));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (childNodes)
|
|
|
|
{
|
|
|
|
PRInt32 j;
|
|
|
|
PRUint32 childCount;
|
|
|
|
childNodes->GetLength(&childCount);
|
2000-02-15 18:54:05 +03:00
|
|
|
for (j=0 ; j < (PRInt32)childCount; j++)
|
2000-02-08 15:53:34 +03:00
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> childNode;
|
|
|
|
res = childNodes->Item(j, getter_AddRefs(childNode));
|
|
|
|
if ((NS_SUCCEEDED(res)) && (childNode))
|
|
|
|
{
|
|
|
|
res = RelativeFontChangeOnNode(aSizeChange, childNode);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// GetPriorHTMLSibling: returns the previous editable sibling, if there is
|
|
|
|
// one within the parent
|
|
|
|
//
|
|
|
|
nsresult
|
|
|
|
nsHTMLEditor::GetPriorHTMLSibling(nsIDOMNode *inNode, nsCOMPtr<nsIDOMNode> *outNode)
|
|
|
|
{
|
|
|
|
if (!outNode || !inNode) return NS_ERROR_NULL_POINTER;
|
|
|
|
nsresult res = NS_OK;
|
|
|
|
*outNode = nsnull;
|
|
|
|
nsCOMPtr<nsIDOMNode> temp, node = do_QueryInterface(inNode);
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
res = node->GetPreviousSibling(getter_AddRefs(temp));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!temp) return NS_OK; // return null sibling
|
|
|
|
// if it's editable, we're done
|
|
|
|
if (IsEditable(temp)) break;
|
|
|
|
// otherwise try again
|
|
|
|
node = temp;
|
|
|
|
}
|
|
|
|
*outNode = temp;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-02-08 15:53:34 +03:00
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
2000-03-24 03:26:47 +03:00
|
|
|
// GetPriorHTMLSibling: returns the previous editable sibling, if there is
|
|
|
|
// one within the parent. just like above routine but
|
|
|
|
// takes a parent/offset instead of a node.
|
|
|
|
//
|
2000-02-08 15:53:34 +03:00
|
|
|
nsresult
|
2000-03-24 03:26:47 +03:00
|
|
|
nsHTMLEditor::GetPriorHTMLSibling(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr<nsIDOMNode> *outNode)
|
2000-02-08 15:53:34 +03:00
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
if (!outNode || !inParent) return NS_ERROR_NULL_POINTER;
|
|
|
|
nsresult res = NS_OK;
|
|
|
|
*outNode = nsnull;
|
|
|
|
if (!inOffset) return NS_OK; // return null sibling if at offset zero
|
|
|
|
nsCOMPtr<nsIDOMNode> node = nsEditor::GetChildAt(inParent,inOffset-1);
|
|
|
|
if (IsEditable(node))
|
|
|
|
{
|
|
|
|
*outNode = node;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
// else
|
|
|
|
return GetPriorHTMLSibling(node, outNode);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// GetNextHTMLSibling: returns the next editable sibling, if there is
|
|
|
|
// one within the parent
|
|
|
|
//
|
|
|
|
nsresult
|
|
|
|
nsHTMLEditor::GetNextHTMLSibling(nsIDOMNode *inNode, nsCOMPtr<nsIDOMNode> *outNode)
|
|
|
|
{
|
|
|
|
if (!outNode) return NS_ERROR_NULL_POINTER;
|
|
|
|
nsresult res = NS_OK;
|
|
|
|
*outNode = nsnull;
|
|
|
|
nsCOMPtr<nsIDOMNode> temp, node = do_QueryInterface(inNode);
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
res = node->GetNextSibling(getter_AddRefs(temp));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!temp) return NS_ERROR_FAILURE;
|
|
|
|
// if it's editable, we're done
|
|
|
|
if (IsEditable(temp)) break;
|
|
|
|
// otherwise try again
|
|
|
|
node = temp;
|
|
|
|
}
|
|
|
|
*outNode = temp;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// GetNextHTMLSibling: returns the next editable sibling, if there is
|
|
|
|
// one within the parent. just like above routine but
|
|
|
|
// takes a parent/offset instead of a node.
|
|
|
|
//
|
|
|
|
nsresult
|
|
|
|
nsHTMLEditor::GetNextHTMLSibling(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr<nsIDOMNode> *outNode)
|
|
|
|
{
|
|
|
|
if (!outNode || !inParent) return NS_ERROR_NULL_POINTER;
|
|
|
|
nsresult res = NS_OK;
|
|
|
|
*outNode = nsnull;
|
|
|
|
nsCOMPtr<nsIDOMNode> node = nsEditor::GetChildAt(inParent,inOffset);
|
|
|
|
if (!node) return NS_OK; // return null sibling if no sibling
|
|
|
|
if (IsEditable(node))
|
|
|
|
{
|
|
|
|
*outNode = node;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
// else
|
|
|
|
return GetPriorHTMLSibling(node, outNode);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// GetPriorHTMLNode: returns the previous editable leaf node, if there is
|
|
|
|
// one within the <body>
|
|
|
|
//
|
|
|
|
nsresult
|
|
|
|
nsHTMLEditor::GetPriorHTMLNode(nsIDOMNode *inNode, nsCOMPtr<nsIDOMNode> *outNode)
|
|
|
|
{
|
|
|
|
if (!outNode) return NS_ERROR_NULL_POINTER;
|
|
|
|
nsresult res = GetPriorNode(inNode, PR_TRUE, getter_AddRefs(*outNode));
|
2000-02-08 15:53:34 +03:00
|
|
|
if (NS_FAILED(res)) return res;
|
2000-03-24 03:26:47 +03:00
|
|
|
|
|
|
|
// if it's not in the body, then zero it out
|
|
|
|
if (*outNode && !nsHTMLEditUtils::InBody(*outNode))
|
|
|
|
{
|
|
|
|
*outNode = nsnull;
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// GetPriorHTMLNode: same as above but takes {parent,offset} instead of node
|
|
|
|
//
|
|
|
|
nsresult
|
|
|
|
nsHTMLEditor::GetPriorHTMLNode(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr<nsIDOMNode> *outNode)
|
|
|
|
{
|
|
|
|
if (!outNode) return NS_ERROR_NULL_POINTER;
|
|
|
|
nsresult res = GetPriorNode(inParent, inOffset, PR_TRUE, getter_AddRefs(*outNode));
|
2000-02-08 15:53:34 +03:00
|
|
|
if (NS_FAILED(res)) return res;
|
2000-03-24 03:26:47 +03:00
|
|
|
|
|
|
|
// if it's not in the body, then zero it out
|
|
|
|
if (*outNode && !nsHTMLEditUtils::InBody(*outNode))
|
2000-02-08 15:53:34 +03:00
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
*outNode = nsnull;
|
2000-02-08 15:53:34 +03:00
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
2000-03-24 03:26:47 +03:00
|
|
|
// GetNextHTMLNode: returns the previous editable leaf node, if there is
|
|
|
|
// one within the <body>
|
|
|
|
//
|
2000-02-08 15:53:34 +03:00
|
|
|
nsresult
|
2000-03-24 03:26:47 +03:00
|
|
|
nsHTMLEditor::GetNextHTMLNode(nsIDOMNode *inNode, nsCOMPtr<nsIDOMNode> *outNode)
|
2000-02-08 15:53:34 +03:00
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
if (!outNode) return NS_ERROR_NULL_POINTER;
|
|
|
|
nsresult res = GetNextNode(inNode, PR_TRUE, getter_AddRefs(*outNode));
|
2000-02-08 15:53:34 +03:00
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
// if it's not in the body, then zero it out
|
|
|
|
if (*outNode && !nsHTMLEditUtils::InBody(*outNode))
|
2000-02-08 15:53:34 +03:00
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
*outNode = nsnull;
|
2000-02-08 15:53:34 +03:00
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
2000-03-24 03:26:47 +03:00
|
|
|
// GetNHTMLextNode: same as above but takes {parent,offset} instead of node
|
|
|
|
//
|
2000-02-08 15:53:34 +03:00
|
|
|
nsresult
|
2000-03-24 03:26:47 +03:00
|
|
|
nsHTMLEditor::GetNextHTMLNode(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr<nsIDOMNode> *outNode)
|
2000-02-08 15:53:34 +03:00
|
|
|
{
|
2000-03-24 03:26:47 +03:00
|
|
|
if (!outNode) return NS_ERROR_NULL_POINTER;
|
|
|
|
nsresult res = GetNextNode(inParent, inOffset, PR_TRUE, getter_AddRefs(*outNode));
|
2000-02-08 15:53:34 +03:00
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
// if it's not in the body, then zero it out
|
|
|
|
if (*outNode && !nsHTMLEditUtils::InBody(*outNode))
|
|
|
|
{
|
|
|
|
*outNode = nsnull;
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsHTMLEditor::IsFirstEditableChild( nsIDOMNode *aNode, PRBool *aOutIsFirst)
|
|
|
|
{
|
|
|
|
// check parms
|
|
|
|
if (!aOutIsFirst || !aNode) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
// init out parms
|
|
|
|
*aOutIsFirst = PR_FALSE;
|
|
|
|
|
|
|
|
// find first editable child and compare it to aNode
|
|
|
|
nsCOMPtr<nsIDOMNode> parent, firstChild;
|
|
|
|
nsresult res = aNode->GetParentNode(getter_AddRefs(parent));
|
2000-02-08 15:53:34 +03:00
|
|
|
if (NS_FAILED(res)) return res;
|
2000-03-24 03:26:47 +03:00
|
|
|
if (!parent) return NS_ERROR_FAILURE;
|
|
|
|
res = GetFirstEditableChild(parent, &firstChild);
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
*aOutIsFirst = (firstChild.get() == aNode);
|
|
|
|
return res;
|
|
|
|
}
|
2000-02-08 15:53:34 +03:00
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsHTMLEditor::IsLastEditableChild( nsIDOMNode *aNode, PRBool *aOutIsLast)
|
|
|
|
{
|
|
|
|
// check parms
|
|
|
|
if (!aOutIsLast || !aNode) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
// init out parms
|
|
|
|
*aOutIsLast = PR_FALSE;
|
|
|
|
|
|
|
|
// find last editable child and compare it to aNode
|
|
|
|
nsCOMPtr<nsIDOMNode> parent, lastChild;
|
|
|
|
nsresult res = aNode->GetParentNode(getter_AddRefs(parent));
|
2000-02-08 15:53:34 +03:00
|
|
|
if (NS_FAILED(res)) return res;
|
2000-03-24 03:26:47 +03:00
|
|
|
if (!parent) return NS_ERROR_FAILURE;
|
|
|
|
res = GetLastEditableChild(parent, &lastChild);
|
2000-02-08 15:53:34 +03:00
|
|
|
if (NS_FAILED(res)) return res;
|
2000-03-24 03:26:47 +03:00
|
|
|
|
|
|
|
*aOutIsLast = (lastChild.get() == aNode);
|
|
|
|
return res;
|
|
|
|
}
|
2000-02-08 15:53:34 +03:00
|
|
|
|
2000-03-24 03:26:47 +03:00
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsHTMLEditor::GetFirstEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutFirstChild)
|
|
|
|
{
|
|
|
|
// check parms
|
|
|
|
if (!aOutFirstChild || !aNode) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
// init out parms
|
|
|
|
*aOutFirstChild = nsnull;
|
|
|
|
|
|
|
|
// find first editable child
|
|
|
|
nsCOMPtr<nsIDOMNode> child;
|
|
|
|
nsresult res = aNode->GetFirstChild(getter_AddRefs(child));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
while (child && !IsEditable(child))
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> tmp;
|
|
|
|
res = child->GetNextSibling(getter_AddRefs(tmp));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!tmp) return NS_ERROR_FAILURE;
|
|
|
|
child = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
*aOutFirstChild = child;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsHTMLEditor::GetLastEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutLastChild)
|
|
|
|
{
|
|
|
|
// check parms
|
|
|
|
if (!aOutLastChild || !aNode) return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
// init out parms
|
|
|
|
*aOutLastChild = nsnull;
|
|
|
|
|
|
|
|
// find last editable child
|
|
|
|
nsCOMPtr<nsIDOMNode> child;
|
|
|
|
nsresult res = aNode->GetLastChild(getter_AddRefs(child));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
|
|
|
|
while (child && !IsEditable(child))
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> tmp;
|
|
|
|
res = child->GetPreviousSibling(getter_AddRefs(tmp));
|
|
|
|
if (NS_FAILED(res)) return res;
|
|
|
|
if (!tmp) return NS_ERROR_FAILURE;
|
|
|
|
child = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
*aOutLastChild = child;
|
|
|
|
return res;
|
2000-02-08 15:53:34 +03:00
|
|
|
}
|
2000-03-24 03:26:47 +03:00
|
|
|
|