This commit is contained in:
jfrancis%netscape.com 2000-05-24 23:00:24 +00:00
Родитель 6630ca37f6
Коммит 18736fd80c
34 изменённых файлов: 1960 добавлений и 696 удалений

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

@ -57,6 +57,7 @@ TypeInState::QueryInterface(REFNSIID aIID, void** aInstancePtr)
TypeInState::TypeInState() :
mSetArray()
,mClearedArray()
,mRelativeFontSize(0)
{
NS_INIT_REFCNT();
Reset();
@ -108,6 +109,18 @@ nsresult TypeInState::SetProp(nsIAtom *aProp, const nsString &aAttr)
nsresult TypeInState::SetProp(nsIAtom *aProp, const nsString &aAttr, const nsString &aValue)
{
// special case for big/small, these nest
if (nsIEditProperty::big == aProp)
{
mRelativeFontSize++;
return NS_OK;
}
if (nsIEditProperty::small == aProp)
{
mRelativeFontSize--;
return NS_OK;
}
// if it's already set we are done
if (IsPropSet(aProp,aAttr,aValue)) return NS_OK;
@ -196,6 +209,17 @@ nsresult TypeInState::TakeSetProperty(PropItem **outPropItem)
return NS_OK;
}
//**************************************************************************
// TakeRelativeFontSize: hands back relative font value, which is then
// cleared out.
nsresult TypeInState::TakeRelativeFontSize(PRInt32 *outRelSize)
{
if (!outRelSize) return NS_ERROR_NULL_POINTER;
*outRelSize = mRelativeFontSize;
mRelativeFontSize = 0;
return NS_OK;
}
nsresult TypeInState::GetTypingState(PRBool &isSet, PRBool &theSetting, nsIAtom *aProp)
{
return GetTypingState(isSet, theSetting, aProp, nsAutoString(), nsAutoString());
@ -248,6 +272,7 @@ nsresult TypeInState::RemovePropFromSetList(nsIAtom *aProp,
if (!aProp)
{
// clear _all_ props
mRelativeFontSize=0;
while ((index = mSetArray.Count()))
{
// go backwards to keep nsVoidArray from memmoving everything each time

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

@ -69,6 +69,11 @@ public:
// caller assumes ownership of PropItem and must delete it.
nsresult TakeSetProperty(PropItem **outPropItem);
//**************************************************************************
// TakeRelativeFontSize: hands back relative font value, which is then
// cleared out.
nsresult TakeRelativeFontSize(PRInt32 *outRelSize);
nsresult GetTypingState(PRBool &isSet, PRBool &theSetting, nsIAtom *aProp);
nsresult GetTypingState(PRBool &isSet, PRBool &theSetting, nsIAtom *aProp,
const nsString &aAttr);
@ -87,6 +92,7 @@ protected:
nsVoidArray mSetArray;
nsVoidArray mClearedArray;
PRInt32 mRelativeFontSize;
};

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

@ -165,40 +165,6 @@ nsBaseStateUpdatingCommand::UpdateCommandState(const PRUnichar *aCommandName, ns
}
#ifdef XP_MAC
#pragma mark -
#endif
NS_IMETHODIMP
nsAlwaysEnabledCommands::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refCon, PRBool *outCmdEnabled)
{
nsCOMPtr<nsIEditorShell> editorShell = do_QueryInterface(refCon);
*outCmdEnabled = (editorShell.get() != nsnull); // enabled if we have an editorShell
return NS_OK;
}
NS_IMETHODIMP
nsAlwaysEnabledCommands::DoCommand(const PRUnichar *aCommand, nsISupports * refCon)
{
nsCOMPtr<nsIEditorShell> editorShell = do_QueryInterface(refCon);
if (!editorShell) return NS_ERROR_NULL_POINTER;
nsresult rv = NS_OK;
nsAutoString cmdString(aCommand);
/*
if (cmdString.EqualsWithConversion("cmd_scrollTop"))
return selCont->CompleteScroll(PR_FALSE);
else if (cmdString.EqualsWithConversion("cmd_scrollBottom"))
return selCont->CompleteScroll(PR_TRUE);
*/
return rv;
}
#ifdef XP_MAC
#pragma mark -
#endif
@ -522,13 +488,13 @@ nsOutdentCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refC
{
nsCOMPtr<nsIEditor> editor;
editorShell->GetEditor(getter_AddRefs(editor));
if (editor)
nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(editor);
if (htmlEditor)
{
// XXX fix me. You can't outdent if you're already at the top level.
//PRBool canOutdent;
//editor->CanIndent("outdent", &canOutdent);
PRBool canIndent, canOutdent;
htmlEditor->GetIndentState(canIndent, canOutdent);
*outCmdEnabled = PR_TRUE;
*outCmdEnabled = canOutdent;
}
}
@ -555,8 +521,21 @@ nsOutdentCommand::DoCommand(const PRUnichar *aCommand, nsISupports * refCon)
#pragma mark -
#endif
nsMultiStateCommand::nsMultiStateCommand()
: nsBaseComposerCommand()
, mGotState(PR_FALSE)
{
}
nsMultiStateCommand::~nsMultiStateCommand()
{
}
NS_IMPL_ISUPPORTS_INHERITED1(nsMultiStateCommand, nsBaseComposerCommand, nsIStateUpdatingControllerCommand);
NS_IMETHODIMP
nsParagraphStateCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refCon, PRBool *outCmdEnabled)
nsMultiStateCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refCon, PRBool *outCmdEnabled)
{
nsCOMPtr<nsIEditorShell> editorShell = do_QueryInterface(refCon);
*outCmdEnabled = PR_FALSE;
@ -566,34 +545,166 @@ nsParagraphStateCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports
editorShell->GetEditor(getter_AddRefs(editor));
if (editor)
{
// should be disabled sometimes, like if the current selection is an image
*outCmdEnabled = PR_TRUE;
}
}
return NS_OK;
return UpdateCommandState(aCommand, refCon);
}
NS_IMETHODIMP
nsParagraphStateCommand::DoCommand(const PRUnichar *aCommand, nsISupports * refCon)
nsMultiStateCommand::DoCommand(const PRUnichar *aCommand, nsISupports * refCon)
{
nsCOMPtr<nsIEditorShell> editorShell = do_QueryInterface(refCon);
nsresult rv = NS_OK;
if (editorShell)
{
// we have to grab the state attribute on our command node to find out
// what format to set the paragraph to
nsAutoString stateAttribute;
rv = GetCommandNodeState(aCommand, editorShell, stateAttribute);
if (NS_FAILED(rv)) return rv;
rv = editorShell->SetParagraphFormat(stateAttribute.GetUnicode());
// we have to grab the state attribute on our command node to find out
// what format to set the paragraph to
nsAutoString stateAttribute;
rv = GetCommandNodeState(aCommand, editorShell, stateAttribute);
if (NS_FAILED(rv)) return rv;
rv = SetState(editorShell, stateAttribute);
}
return rv;
}
NS_IMETHODIMP
nsMultiStateCommand::UpdateCommandState(const PRUnichar *aCommandName, nsISupports * refCon)
{
nsCOMPtr<nsIEditorShell> editorShell = do_QueryInterface(refCon);
nsresult rv = NS_OK;
if (editorShell)
{
nsString curFormat;
PRBool isMixed;
rv = GetCurrentState(editorShell, curFormat, isMixed);
if (NS_FAILED(rv)) return rv;
if (isMixed)
curFormat.AssignWithConversion("mixed");
if (!mGotState || (curFormat != mStateString))
{
// poke the UI
SetCommandNodeState(aCommandName, editorShell, curFormat);
mGotState = PR_TRUE;
mStateString = curFormat;
}
}
return rv;
}
#ifdef XP_MAC
#pragma mark -
#endif
nsParagraphStateCommand::nsParagraphStateCommand()
: nsMultiStateCommand()
{
}
nsresult
nsParagraphStateCommand::GetCurrentState(nsIEditorShell *aEditorShell, nsString& outStateString, PRBool& outMixed)
{
NS_ASSERTION(aEditorShell, "Need an editor shell here");
nsCOMPtr<nsIEditor> editor;
aEditorShell->GetEditor(getter_AddRefs(editor));
nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(editor);
if (!htmlEditor) return NS_ERROR_FAILURE;
return htmlEditor->GetParagraphState(outMixed, outStateString);
}
nsresult
nsParagraphStateCommand::SetState(nsIEditorShell *aEditorShell, nsString& newState)
{
NS_ASSERTION(aEditorShell, "Need an editor shell here");
nsCOMPtr<nsIEditor> editor;
aEditorShell->GetEditor(getter_AddRefs(editor));
nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(editor);
if (!htmlEditor) return NS_ERROR_FAILURE;
return htmlEditor->SetParagraphFormat(newState);
}
#ifdef XP_MAC
#pragma mark -
#endif
nsFontFaceStateCommand::nsFontFaceStateCommand()
: nsMultiStateCommand()
{
}
nsresult
nsFontFaceStateCommand::GetCurrentState(nsIEditorShell *aEditorShell, nsString& outStateString, PRBool& outMixed)
{
NS_ASSERTION(aEditorShell, "Need an editor shell here");
nsCOMPtr<nsIEditor> editor;
aEditorShell->GetEditor(getter_AddRefs(editor));
nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(editor);
if (!htmlEditor) return NS_ERROR_FAILURE;
return htmlEditor->GetFontFaceState(outMixed, outStateString);
}
nsresult
nsFontFaceStateCommand::SetState(nsIEditorShell *aEditorShell, nsString& newState)
{
NS_ASSERTION(aEditorShell, "Need an editor shell here");
nsCOMPtr<nsIEditor> editor;
aEditorShell->GetEditor(getter_AddRefs(editor));
nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(editor);
if (!htmlEditor) return NS_ERROR_FAILURE;
nsresult rv;
NS_ConvertASCIItoUCS2 emptyString("");
NS_ConvertASCIItoUCS2 fontString("font");
NS_ConvertASCIItoUCS2 faceString("face");
nsCOMPtr<nsIAtom> ttAtom = getter_AddRefs(NS_NewAtom("tt"));
nsCOMPtr<nsIAtom> fontAtom = getter_AddRefs(NS_NewAtom("font"));
if (newState.EqualsWithConversion("tt"))
{
// The old "teletype" attribute
rv = htmlEditor->SetInlineProperty(ttAtom, &emptyString, &emptyString);
// Clear existing font face
rv = htmlEditor->RemoveInlineProperty(fontAtom, &faceString);
}
else
{
// Remove any existing TT nodes
rv = htmlEditor->RemoveInlineProperty(ttAtom, &emptyString);
if (newState == emptyString || newState.EqualsWithConversion("normal")) {
rv = htmlEditor->RemoveInlineProperty(fontAtom, &faceString);
} else {
rv = htmlEditor->SetInlineProperty(fontAtom, &faceString, &newState);
}
}
return rv;
}
#ifdef XP_MAC
#pragma mark -
#endif

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

@ -66,7 +66,7 @@ public: \
NS_DECL_NSICONTROLLERCOMMAND \
};
// virtual base class for commands that need to save and update state
// virtual base class for commands that need to save and update Boolean state (like styles etc)
class nsBaseStateUpdatingCommand : public nsBaseComposerCommand,
public nsIStateUpdatingControllerCommand
{
@ -98,7 +98,8 @@ protected:
};
// shared class for the various style updating commands
// Shared class for the various style updating commands like bold, italics etc.
// Suitable for commands whose state is either 'on' or 'off'.
class nsStyleUpdatingCommand : public nsBaseStateUpdatingCommand
{
public:
@ -132,8 +133,58 @@ protected:
};
// Base class for commands whose state consists of a string (e.g. para format)
class nsMultiStateCommand : public nsBaseComposerCommand,
public nsIStateUpdatingControllerCommand
{
public:
nsMultiStateCommand();
virtual ~nsMultiStateCommand();
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSICONTROLLERCOMMAND
NS_DECL_NSISTATEUPDATINGCONTROLLERCOMMAND
protected:
virtual nsresult GetCurrentState(nsIEditorShell *aEditorShell, nsString& outStateString, PRBool& outMixed) = 0;
virtual nsresult SetState(nsIEditorShell *aEditorShell, nsString& newState) = 0;
protected:
PRPackedBool mGotState;
nsString mStateString;
};
class nsParagraphStateCommand : public nsMultiStateCommand
{
public:
nsParagraphStateCommand();
protected:
virtual nsresult GetCurrentState(nsIEditorShell *aEditorShell, nsString& outStateString, PRBool& outMixed);
virtual nsresult SetState(nsIEditorShell *aEditorShell, nsString& newState);
};
class nsFontFaceStateCommand : public nsMultiStateCommand
{
public:
nsFontFaceStateCommand();
protected:
virtual nsresult GetCurrentState(nsIEditorShell *aEditorShell, nsString& outStateString, PRBool& outMixed);
virtual nsresult SetState(nsIEditorShell *aEditorShell, nsString& newState);
};
// composer commands
NS_DECL_COMPOSER_COMMAND(nsAlwaysEnabledCommands)
NS_DECL_COMPOSER_COMMAND(nsCloseCommand)
NS_DECL_COMPOSER_COMMAND(nsPrintingCommands)
@ -157,7 +208,6 @@ NS_DECL_COMPOSER_COMMAND(nsPasteQuotationCommand)
NS_DECL_COMPOSER_COMMAND(nsIndentCommand)
NS_DECL_COMPOSER_COMMAND(nsOutdentCommand)
NS_DECL_COMPOSER_COMMAND(nsParagraphStateCommand)
NS_DECL_COMPOSER_COMMAND(nsAlignCommand)
NS_DECL_COMPOSER_COMMAND(nsRemoveStylesCommand)
NS_DECL_COMPOSER_COMMAND(nsIncreaseFontSizeCommand)

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

@ -3695,6 +3695,23 @@ nsEditor::NodeIsType(nsIDOMNode *aNode, nsIAtom *aTag)
return PR_FALSE;
}
PRBool
nsEditor::NodeIsType(nsIDOMNode *aNode, const nsString &aTagStr)
{
nsCOMPtr<nsIDOMElement>element;
element = do_QueryInterface(aNode);
if (element)
{
nsAutoString tag;
element->GetTagName(tag);
if (tag.EqualsIgnoreCase(aTagStr))
{
return PR_TRUE;
}
}
return PR_FALSE;
}
PRBool
nsEditor::CanContainTag(nsIDOMNode* aParent, const nsString &aChildTag)
{

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

@ -598,6 +598,7 @@ public:
/** returns PR_TRUE if aNode is of the type implied by aTag */
static PRBool NodeIsType(nsIDOMNode *aNode, nsIAtom *aTag);
static PRBool NodeIsType(nsIDOMNode *aNode, const nsString &aTag);
/** returns PR_TRUE if aParent can contain a child of type aTag */
PRBool CanContainTag(nsIDOMNode* aParent, const nsString &aTag);

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

@ -327,6 +327,8 @@ nsresult nsComposerController::RegisterComposerCommands(nsIControllerCommandMana
// format stuff
NS_REGISTER_ONE_COMMAND(nsParagraphStateCommand, "cmd_paragraphState");
NS_REGISTER_ONE_COMMAND(nsFontFaceStateCommand, "cmd_fontFace");
NS_REGISTER_ONE_COMMAND(nsAlignCommand, "cmd_align");
NS_REGISTER_ONE_COMMAND(nsRemoveStylesCommand, "cmd_removeStyles");
NS_REGISTER_ONE_COMMAND(nsIncreaseFontSizeCommand, "cmd_increaseFont");

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

@ -79,7 +79,6 @@ nsHTMLEditRules::nsHTMLEditRules() :
mDocChangeRange(nsnull)
,mListenerEnabled(PR_TRUE)
,mUtilRange(nsnull)
,mBody(nsnull)
,mJoinOffset(0)
{
}
@ -192,73 +191,8 @@ nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection)
nsresult res = NS_OK;
if (!--mActionNesting)
{
ConfirmSelectionInBody();
if (action == nsEditor::kOpIgnore) return NS_OK;
nsCOMPtr<nsIDOMSelection>selection;
res = mEditor->GetSelection(getter_AddRefs(selection));
if (NS_FAILED(res)) return res;
if (mDocChangeRange && !((action == nsEditor::kOpUndo) || (action == nsEditor::kOpRedo)))
{
// dont let any txns in here move the selection around behind our back.
// Note that this won't prevent explicit selection setting from working.
nsAutoTxnsConserveSelection dontSpazMySelection(mEditor);
// expand the "changed doc range" as needed
res = PromoteRange(mDocChangeRange, action);
if (NS_FAILED(res)) return res;
// add in any needed <br>s, and remove any unneeded ones.
res = AdjustSpecialBreaks();
if (NS_FAILED(res)) return res;
// merge any adjacent text nodes
if ( (action != nsEditor::kOpInsertText &&
action != nsEditor::kOpInsertIMEText) )
{
res = mEditor->CollapseAdjacentTextNodes(mDocChangeRange);
if (NS_FAILED(res)) return res;
}
// adjust whitespace for insert text and delete actions
if ((action == nsEditor::kOpInsertText) ||
(action == nsEditor::kOpInsertIMEText) ||
(action == nsEditor::kOpDeleteSelection))
{
res = AdjustWhitespace(selection);
if (NS_FAILED(res)) return res;
}
// replace newlines that are preformatted
// MOOSE: This is buttUgly. A better way to
// organize the action enum is in order.
if ((action == nsEditor::kOpInsertText) ||
(action == nsEditor::kOpInsertIMEText) ||
(action == nsHTMLEditor::kOpInsertElement) ||
(action == nsHTMLEditor::kOpInsertQuotation) ||
(action == nsEditor::kOpInsertNode))
{
res = ReplaceNewlines(mDocChangeRange);
}
// clean up any empty nodes in the selection
res = RemoveEmptyNodes();
if (NS_FAILED(res)) return res;
// adjust selection for insert text and delete actions
if ((action == nsEditor::kOpInsertText) ||
(action == nsEditor::kOpInsertIMEText) ||
(action == nsEditor::kOpDeleteSelection))
{
res = AdjustSelection(selection, aDirection);
if (NS_FAILED(res)) return res;
}
}
// detect empty doc
res = CreateBogusNodeIfNeeded(selection);
// do all the tricky stuff
res = AfterEditInner(action, aDirection);
// turn on caret
nsCOMPtr<nsISelectionController> selCon;
mEditor->GetSelectionController(getter_AddRefs(selCon));
@ -269,6 +203,79 @@ nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection)
}
nsresult
nsHTMLEditRules::AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection)
{
ConfirmSelectionInBody();
if (action == nsEditor::kOpIgnore) return NS_OK;
nsCOMPtr<nsIDOMSelection>selection;
nsresult res = mEditor->GetSelection(getter_AddRefs(selection));
if (NS_FAILED(res)) return res;
if (mDocChangeRange && !((action == nsEditor::kOpUndo) || (action == nsEditor::kOpRedo)))
{
// dont let any txns in here move the selection around behind our back.
// Note that this won't prevent explicit selection setting from working.
nsAutoTxnsConserveSelection dontSpazMySelection(mEditor);
// expand the "changed doc range" as needed
res = PromoteRange(mDocChangeRange, action);
if (NS_FAILED(res)) return res;
// add in any needed <br>s, and remove any unneeded ones.
res = AdjustSpecialBreaks();
if (NS_FAILED(res)) return res;
// merge any adjacent text nodes
if ( (action != nsEditor::kOpInsertText &&
action != nsEditor::kOpInsertIMEText) )
{
res = mEditor->CollapseAdjacentTextNodes(mDocChangeRange);
if (NS_FAILED(res)) return res;
}
// adjust whitespace for insert text and delete actions
if ((action == nsEditor::kOpInsertText) ||
(action == nsEditor::kOpInsertIMEText) ||
(action == nsEditor::kOpDeleteSelection))
{
res = AdjustWhitespace(selection);
if (NS_FAILED(res)) return res;
}
// replace newlines that are preformatted
// MOOSE: This is buttUgly. A better way to
// organize the action enum is in order.
if ((action == nsEditor::kOpInsertText) ||
(action == nsEditor::kOpInsertIMEText) ||
(action == nsHTMLEditor::kOpInsertElement) ||
(action == nsHTMLEditor::kOpInsertQuotation) ||
(action == nsEditor::kOpInsertNode))
{
res = ReplaceNewlines(mDocChangeRange);
}
// clean up any empty nodes in the selection
res = RemoveEmptyNodes();
if (NS_FAILED(res)) return res;
// adjust selection for insert text and delete actions
if ((action == nsEditor::kOpInsertText) ||
(action == nsEditor::kOpInsertIMEText) ||
(action == nsEditor::kOpDeleteSelection))
{
res = AdjustSelection(selection, aDirection);
if (NS_FAILED(res)) return res;
}
}
// detect empty doc
res = CreateBogusNodeIfNeeded(selection);
return res;
}
NS_IMETHODIMP
nsHTMLEditRules::WillDoAction(nsIDOMSelection *aSelection,
nsRulesInfo *aInfo,
@ -303,7 +310,7 @@ nsHTMLEditRules::WillDoAction(nsIDOMSelection *aSelection,
case kDeleteSelection:
return WillDeleteSelection(aSelection, info->collapsedAction, aCancel, aHandled);
case kMakeList:
return WillMakeList(aSelection, info->bOrdered, aCancel, aHandled);
return WillMakeList(aSelection, info->blockType, aCancel, aHandled);
case kIndent:
return WillIndent(aSelection, aCancel, aHandled);
case kOutdent:
@ -314,6 +321,8 @@ nsHTMLEditRules::WillDoAction(nsIDOMSelection *aSelection,
return WillMakeBasicBlock(aSelection, info->blockType, aCancel, aHandled);
case kRemoveList:
return WillRemoveList(aSelection, info->bOrdered, aCancel, aHandled);
case kMakeDefListItem:
return WillMakeDefListItem(aSelection, info->blockType, aCancel, aHandled);
case kInsertElement:
return WillInsert(aSelection, aCancel);
}
@ -349,10 +358,10 @@ nsHTMLEditRules::GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL)
PRBool bNonList = PR_FALSE;
nsCOMPtr<nsISupportsArray> arrayOfNodes;
nsresult res = GetListActionNodes(&arrayOfNodes);
nsresult res = GetListActionNodes(&arrayOfNodes, PR_TRUE);
if (NS_FAILED(res)) return res;
// yummy yummy yummy i've got nodes in my tummy
// examine list type for nodes in selection
PRUint32 listCount;
PRInt32 i;
arrayOfNodes->Count(&listCount);
@ -389,7 +398,102 @@ nsHTMLEditRules::GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL)
NS_IMETHODIMP
nsHTMLEditRules::GetIndentState(PRBool &aCanIndent, PRBool &aCanOutdent)
{
return NS_ERROR_FAILURE;
aCanIndent = PR_TRUE;
aCanOutdent = PR_FALSE;
nsCOMPtr<nsISupportsArray> arrayOfNodes;
nsresult res = GetListActionNodes(&arrayOfNodes, PR_TRUE);
if (NS_FAILED(res)) return res;
// examine nodes in selection for blockquotes or list elements;
// these we can outdent. Note that we return true for canOutdent
// if *any* of the selection is outdentable, rather than all of it.
PRUint32 listCount;
PRInt32 i;
arrayOfNodes->Count(&listCount);
for (i=(PRInt32)listCount-1; i>=0; i--)
{
nsCOMPtr<nsISupports> isupports = (dont_AddRef)(arrayOfNodes->ElementAt(i));
nsCOMPtr<nsIDOMNode> curNode( do_QueryInterface(isupports) );
if (nsHTMLEditUtils::IsList(curNode) ||
nsHTMLEditUtils::IsListItem(curNode) ||
nsHTMLEditUtils::IsBlockquote(curNode))
{
aCanOutdent = PR_TRUE;
break;
}
}
return res;
}
NS_IMETHODIMP
nsHTMLEditRules::GetParagraphState(PRBool &aMixed, nsString &outFormat)
{
// This routine is *heavily* tied to our ui choices in the paragraph
// style popup. I cant see a way around that.
aMixed = PR_TRUE;
outFormat.AssignWithConversion("");
PRBool bMixed = PR_FALSE;
nsAutoString formatStr;
// using "x" as anuninitialized value, since "" is meaningful
formatStr.AssignWithConversion("x");
nsCOMPtr<nsISupportsArray> arrayOfNodes;
nsresult res = GetParagraphFormatNodes(&arrayOfNodes, PR_TRUE);
if (NS_FAILED(res)) return res;
// loop through the nodes in selection and examine their paragraph format
PRUint32 listCount;
PRInt32 i;
arrayOfNodes->Count(&listCount);
for (i=(PRInt32)listCount-1; i>=0; i--)
{
nsCOMPtr<nsISupports> isupports = (dont_AddRef)(arrayOfNodes->ElementAt(i));
nsCOMPtr<nsIDOMNode> curNode( do_QueryInterface(isupports) );
nsAutoString format;
nsCOMPtr<nsIAtom> atom = mEditor->GetTag(curNode);
if (mEditor->IsInlineNode(curNode))
format.AssignWithConversion("");
else if (nsIEditProperty::p == atom)
format.AssignWithConversion("P");
else if (nsHTMLEditUtils::IsHeader(curNode))
{
nsAutoString tag;
nsEditor::GetTagString(curNode,tag);
tag.ToUpperCase();
format = tag;
}
else if (nsIEditProperty::blockquote == atom)
format.AssignWithConversion("BLOCKQUOTE");
else if (nsIEditProperty::address == atom)
format.AssignWithConversion("ADDRESS");
else if (nsIEditProperty::pre == atom)
format.AssignWithConversion("PRE");
else if (nsIEditProperty::dt == atom)
format.AssignWithConversion("DT");
else if (nsIEditProperty::dd == atom)
format.AssignWithConversion("DD");
// if this is the first node, we've found, remember it as the format
if (formatStr.EqualsWithConversion("x"))
formatStr = format;
// else make sure it matches previously found format
else if (format != formatStr)
{
bMixed = PR_TRUE;
break;
}
}
aMixed = bMixed;
outFormat = formatStr;
return res;
}
@ -825,19 +929,30 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection,
}
}
}
// is prior node inline and same type? (This shouldn't happen)
if ( mEditor->HasSameBlockNodeParent(node, priorNode) &&
( mEditor->IsInlineNode(node) || mEditor->IsTextNode(node) ) &&
mEditor->NodesSameType(node, priorNode) )
// is prior node a text node?
else if ( mEditor->IsTextNode(priorNode) )
{
// if so, join them!
nsCOMPtr<nsIDOMNode> topParent;
priorNode->GetParentNode(getter_AddRefs(topParent));
*aHandled = PR_TRUE;
res = JoinNodesSmart(priorNode,node,&selNode,&selOffset);
// delete last character
nsCOMPtr<nsIDOMCharacterData>nodeAsText;
nodeAsText = do_QueryInterface(priorNode);
nodeAsText->GetLength((PRUint32*)&offset);
res = aSelection->Collapse(priorNode,offset);
// just return without setting handled to true.
// default code will take care of actual deletion
return res;
}
else if ( mEditor->IsInlineNode(priorNode) )
{
// remember where we are
res = mEditor->GetNodeLocation(priorNode, &node, &offset);
if (NS_FAILED(res)) return res;
// delete it
res = mEditor->DeleteNode(priorNode);
if (NS_FAILED(res)) return res;
// we did something, so lets say so.
*aHandled = PR_TRUE;
// fix up selection
res = aSelection->Collapse(selNode,selOffset);
res = aSelection->Collapse(node,offset);
return res;
}
else return NS_OK; // punt to default
@ -924,19 +1039,30 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection,
}
}
}
// is next node inline and same type? (This shouldn't happen)
if ( mEditor->HasSameBlockNodeParent(node, nextNode) &&
( mEditor->IsInlineNode(node) || mEditor->IsTextNode(node) ) &&
mEditor->NodesSameType(node, nextNode) )
// is next node a text node?
else if ( mEditor->IsTextNode(nextNode) )
{
// if so, join them!
nsCOMPtr<nsIDOMNode> topParent;
nextNode->GetParentNode(getter_AddRefs(topParent));
*aHandled = PR_TRUE;
res = JoinNodesSmart(node,nextNode,&selNode,&selOffset);
// delete last character
nsCOMPtr<nsIDOMCharacterData>nodeAsText;
nodeAsText = do_QueryInterface(nextNode);
nodeAsText->GetLength((PRUint32*)&offset);
res = aSelection->Collapse(nextNode,offset);
// just return without setting handled to true.
// default code will take care of actual deletion
return res;
}
else if ( mEditor->IsInlineNode(nextNode) )
{
// remember where we are
res = mEditor->GetNodeLocation(nextNode, &node, &offset);
if (NS_FAILED(res)) return res;
// delete it
res = mEditor->DeleteNode(nextNode);
if (NS_FAILED(res)) return res;
// we did something, so lets say so.
*aHandled = PR_TRUE;
// fix up selection
res = aSelection->Collapse(selNode,selOffset);
res = aSelection->Collapse(node,offset);
return res;
}
else return NS_OK; // punt to default
@ -1001,6 +1127,11 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection,
if (NS_FAILED(res)) return res;
if (!nodeToDelete) return NS_ERROR_NULL_POINTER;
if (mBody == nodeToDelete)
{
*aCancel = PR_TRUE;
return res;
}
// if this node is text node, adjust selection
if (nsEditor::IsTextNode(nodeToDelete))
@ -1153,28 +1284,32 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection,
nsresult
nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection,
PRBool aOrdered,
const nsString *aListType,
PRBool *aCancel,
PRBool *aHandled)
PRBool *aHandled,
const nsString *aItemType)
{
if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
if (!aSelection || !aListType || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
nsresult res = WillInsert(aSelection, aCancel);
if (NS_FAILED(res)) return res;
nsAutoString listType;
listType.AssignWithConversion( aOrdered ? "ol" : "ul" );
// initialize out param
// we want to ignore result of WillInsert()
*aCancel = PR_FALSE;
*aHandled = PR_FALSE;
nsAutoString blockType;
blockType.AssignWithConversion( aOrdered ? "ol" : "ul");
// deduce what tag to use for list items
nsAutoString itemType;
if (aItemType)
itemType = *aItemType;
else if (aListType->EqualsWithConversion("dl"))
itemType.AssignWithConversion("dd");
else
itemType.AssignWithConversion("li");
PRBool outMakeEmpty;
res = ShouldMakeEmptyBlock(aSelection, &blockType, &outMakeEmpty);
res = ShouldMakeEmptyBlock(aSelection, aListType, &outMakeEmpty);
if (NS_FAILED(res)) return res;
if (outMakeEmpty)
{
@ -1186,11 +1321,11 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection,
if (NS_FAILED(res)) return res;
// make sure we can put a list here
res = SplitAsNeeded(&listType, &parent, &offset);
res = SplitAsNeeded(aListType, &parent, &offset);
if (NS_FAILED(res)) return res;
res = mEditor->CreateNode(listType, parent, offset, getter_AddRefs(theList));
res = mEditor->CreateNode(*aListType, parent, offset, getter_AddRefs(theList));
if (NS_FAILED(res)) return res;
res = mEditor->CreateNode(NS_ConvertASCIItoUCS2("li"), theList, 0, getter_AddRefs(theListItem));
res = mEditor->CreateNode(itemType, theList, 0, getter_AddRefs(theListItem));
if (NS_FAILED(res)) return res;
// put selection in new list item
res = aSelection->Collapse(theListItem,0);
@ -1224,8 +1359,7 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection,
nsCOMPtr<nsIDOMNode> curNode( do_QueryInterface(isupports) );
while (nsHTMLEditUtils::IsDiv(curNode)
|| nsHTMLEditUtils::IsOrderedList(curNode)
|| nsHTMLEditUtils::IsUnorderedList(curNode)
|| nsHTMLEditUtils::IsList(curNode)
|| nsHTMLEditUtils::IsBlockquote(curNode))
{
// dive as long as there is only one child, and it is a list, div, blockquote
@ -1238,8 +1372,7 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection,
// keep diving
nsCOMPtr <nsIDOMNode> tmpNode = nsEditor::GetChildAt(curNode, 0);
if (nsHTMLEditUtils::IsDiv(tmpNode)
|| nsHTMLEditUtils::IsOrderedList(tmpNode)
|| nsHTMLEditUtils::IsUnorderedList(tmpNode)
|| nsHTMLEditUtils::IsList(tmpNode)
|| nsHTMLEditUtils::IsBlockquote(tmpNode))
{
// check editablility XXX floppy moose
@ -1305,37 +1438,38 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection,
if (nsHTMLEditUtils::IsList(curNode))
{
nsAutoString existingListStr;
res = mEditor->GetTagString(curNode, existingListStr);
// do we have a curList already?
if (curList && !nsHTMLEditUtils::IsDescendantOf(curNode, curList))
{
// move all of our dachildren into curList.
// move all of our children into curList.
// cheezy way to do it: move whole list and then
// RemoveContainer() on the list
// RemoveContainer() on the list.
// ConvertListType first: that routine
// handles converting the list item types, if needed
res = mEditor->MoveNode(curNode, curList, -1);
if (NS_FAILED(res)) return res;
res = mEditor->RemoveContainer(curNode);
res = ConvertListType(curNode, &newBlock, *aListType, itemType);
if (NS_FAILED(res)) return res;
}
else if ( (nsHTMLEditUtils::IsUnorderedList(curNode) && aOrdered) ||
(nsHTMLEditUtils::IsOrderedList(curNode) && !aOrdered) )
{
// replace list with new list type
res = mEditor->ReplaceContainer(curNode,&newBlock,blockType);
res = mEditor->RemoveContainer(newBlock);
if (NS_FAILED(res)) return res;
curList = newBlock;
}
else
{
curList = curNode;
// replace list with new list type
res = ConvertListType(curNode, &newBlock, *aListType, itemType);
if (NS_FAILED(res)) return res;
curList = newBlock;
}
continue;
}
if (nsHTMLEditUtils::IsListItem(curNode))
{
if ( (nsHTMLEditUtils::IsUnorderedList(curParent) && aOrdered) ||
(nsHTMLEditUtils::IsOrderedList(curParent) && !aOrdered) )
nsAutoString existingListStr;
res = mEditor->GetTagString(curParent, existingListStr);
if ( existingListStr != *aListType )
{
// list item is in wrong type of list.
// if we dont have a curList, split the old list
@ -1348,16 +1482,23 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection,
PRInt32 o;
res = nsEditor::GetNodeLocation(curParent, &p, &o);
if (NS_FAILED(res)) return res;
res = mEditor->CreateNode(listType, p, o, getter_AddRefs(curList));
res = mEditor->CreateNode(*aListType, p, o, getter_AddRefs(curList));
if (NS_FAILED(res)) return res;
}
// move list item to new list
res = mEditor->MoveNode(curNode, curList, -1);
if (NS_FAILED(res)) return res;
// convert list item type if needed
if (!mEditor->NodeIsType(curNode,itemType))
{
res = mEditor->ReplaceContainer(curNode, &newBlock, itemType);
if (NS_FAILED(res)) return res;
}
}
else
{
// item is in right type of list. But we might still have to move it.
// and we might need to convert list item types.
if (!curList)
curList = curParent;
else
@ -1369,6 +1510,11 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection,
if (NS_FAILED(res)) return res;
}
}
if (!mEditor->NodeIsType(curNode,itemType))
{
res = mEditor->ReplaceContainer(curNode, &newBlock, itemType);
if (NS_FAILED(res)) return res;
}
}
continue;
}
@ -1377,9 +1523,9 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection,
// or if this node doesn't go in list we used earlier.
if (!curList) // || transitionList[i])
{
res = SplitAsNeeded(&listType, &curParent, &offset);
res = SplitAsNeeded(aListType, &curParent, &offset);
if (NS_FAILED(res)) return res;
res = mEditor->CreateNode(listType, curParent, offset, getter_AddRefs(curList));
res = mEditor->CreateNode(*aListType, curParent, offset, getter_AddRefs(curList));
if (NS_FAILED(res)) return res;
// curList is now the correct thing to put curNode in
prevListItem = 0;
@ -1404,11 +1550,11 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection,
// don't wrap li around a paragraph. instead replace paragraph with li
if (nsHTMLEditUtils::IsParagraph(curNode))
{
res = mEditor->ReplaceContainer(curNode, &listItem, NS_ConvertASCIItoUCS2("li"));
res = mEditor->ReplaceContainer(curNode, &listItem, itemType);
}
else
{
res = mEditor->InsertContainerAbove(curNode, &listItem, NS_ConvertASCIItoUCS2("li"));
res = mEditor->InsertContainerAbove(curNode, &listItem, itemType);
}
if (NS_FAILED(res)) return res;
if (nsEditor::IsInlineNode(curNode))
@ -1525,6 +1671,18 @@ nsHTMLEditRules::WillRemoveList(nsIDOMSelection *aSelection,
}
nsresult
nsHTMLEditRules::WillMakeDefListItem(nsIDOMSelection *aSelection,
const nsString *aItemType,
PRBool *aCancel,
PRBool *aHandled)
{
// for now we let WillMakeList handle this
nsAutoString listType;
listType.AssignWithConversion("dl");
return WillMakeList(aSelection, &listType, aCancel, aHandled, aItemType);
}
nsresult
nsHTMLEditRules::WillMakeBasicBlock(nsIDOMSelection *aSelection,
const nsString *aBlockType,
@ -1808,6 +1966,40 @@ nsHTMLEditRules::WillOutdent(nsIDOMSelection *aSelection, PRBool *aCancel, PRBoo
}
///////////////////////////////////////////////////////////////////////////
// ConvertListType: convert list type and list item type.
//
//
nsresult
nsHTMLEditRules::ConvertListType(nsIDOMNode *aList,
nsCOMPtr<nsIDOMNode> *outList,
const nsString& aListType,
const nsString& aItemType)
{
if (!aList || !outList) return NS_ERROR_NULL_POINTER;
*outList = aList; // we migvht not need to change the list
nsresult res = NS_OK;
nsCOMPtr<nsIDOMNode> child, temp;
aList->GetFirstChild(getter_AddRefs(child));
while (child)
{
if (!mEditor->NodeIsType(child, aItemType))
{
res = mEditor->ReplaceContainer(child, &temp, aItemType);
if (NS_FAILED(res)) return res;
child = temp;
}
child->GetNextSibling(getter_AddRefs(temp));
child = temp;
}
if (!mEditor->NodeIsType(aList, aListType))
{
res = mEditor->ReplaceContainer(aList, outList, aListType);
}
return res;
}
///////////////////////////////////////////////////////////////////////////
// CreateStyleForInsertText: take care of clearing and setting appropriate
// style nodes for text insertion.
@ -1839,10 +2031,15 @@ nsHTMLEditRules::CreateStyleForInsertText(nsIDOMSelection *aSelection, nsIDOMDoc
}
// then process setting any styles
mEditor->mTypeInState->TakeSetProperty(&item);
PRInt32 relFontSize;
if (item) // we have at least one style to add; make a
{ // new text node to insert style nodes above.
res = mEditor->mTypeInState->TakeRelativeFontSize(&relFontSize);
if (NS_FAILED(res)) return res;
res = mEditor->mTypeInState->TakeSetProperty(&item);
if (NS_FAILED(res)) return res;
if (item || relFontSize) // we have at least one style to add; make a
{ // new text node to insert style nodes above.
if (mEditor->IsTextNode(node))
{
// if we are in a text node, split it
@ -1863,17 +2060,29 @@ nsHTMLEditRules::CreateStyleForInsertText(nsIDOMSelection *aSelection, nsIDOMDoc
node = newNode;
offset = 0;
weDidSometing = PR_TRUE;
if (relFontSize)
{
PRInt32 j, dir;
// dir indicated bigger versus smaller. 1 = bigger, -1 = smaller
if (relFontSize > 0) dir=1;
else dir = -1;
for (j=0; j<abs(relFontSize); j++)
{
res = mEditor->RelativeFontChangeOnTextNode(dir, nodeAsText, 0, -1);
if (NS_FAILED(res)) return res;
}
}
while (item)
{
res = mEditor->SetInlinePropertyOnNode(node, item->tag, &item->attr, &item->value);
if (NS_FAILED(res)) return res;
// we own item now (TakeSetProperty hands ownership to us)
delete item;
mEditor->mTypeInState->TakeSetProperty(&item);
}
}
while (item)
{
res = mEditor->SetInlinePropertyOnNode(node, item->tag, &item->attr, &item->value);
if (NS_FAILED(res)) return res;
// we own item now (TakeSetProperty hands ownership to us)
delete item;
mEditor->mTypeInState->TakeSetProperty(&item);
}
if (weDidSometing)
return aSelection->Collapse(node, offset);
@ -2616,7 +2825,8 @@ nsHTMLEditRules::PromoteRange(nsIDOMRange *inRange,
nsresult
nsHTMLEditRules::GetNodesForOperation(nsISupportsArray *inArrayOfRanges,
nsCOMPtr<nsISupportsArray> *outArrayOfNodes,
PRInt32 inOperationType)
PRInt32 inOperationType,
PRBool aDontTouchContent)
{
if (!inArrayOfRanges || !outArrayOfNodes) return NS_ERROR_NULL_POINTER;
@ -2669,7 +2879,7 @@ nsHTMLEditRules::GetNodesForOperation(nsISupportsArray *inArrayOfRanges,
{
nsCOMPtr<nsISupports> isupports = (dont_AddRef)((*outArrayOfNodes)->ElementAt(i));
nsCOMPtr<nsIDOMNode> node( do_QueryInterface(isupports) );
if (mEditor->IsInlineNode(node) && mEditor->IsContainer(node))
if (!aDontTouchContent && mEditor->IsInlineNode(node) && mEditor->IsContainer(node))
{
nsCOMPtr<nsISupportsArray> arrayOfInlines;
res = BustUpInlinesAtBRs(node, &arrayOfInlines);
@ -2735,7 +2945,8 @@ nsHTMLEditRules::GetChildNodesForOperation(nsIDOMNode *inNode,
// GetListActionNodes:
//
nsresult
nsHTMLEditRules::GetListActionNodes(nsCOMPtr<nsISupportsArray> *outArrayOfNodes)
nsHTMLEditRules::GetListActionNodes(nsCOMPtr<nsISupportsArray> *outArrayOfNodes,
PRBool aDontTouchContent)
{
if (!outArrayOfNodes) return NS_ERROR_NULL_POINTER;
@ -2748,7 +2959,58 @@ nsHTMLEditRules::GetListActionNodes(nsCOMPtr<nsISupportsArray> *outArrayOfNodes)
if (NS_FAILED(res)) return res;
// use these ranges to contruct a list of nodes to act on.
res = GetNodesForOperation(arrayOfRanges, outArrayOfNodes, kMakeList);
res = GetNodesForOperation(arrayOfRanges, outArrayOfNodes, kMakeList, aDontTouchContent);
if (NS_FAILED(res)) return res;
// pre process our list of nodes...
PRUint32 listCount;
PRInt32 i;
(*outArrayOfNodes)->Count(&listCount);
for (i=(PRInt32)listCount-1; i>=0; i--)
{
nsCOMPtr<nsISupports> isupports = (dont_AddRef)((*outArrayOfNodes)->ElementAt(i));
nsCOMPtr<nsIDOMNode> testNode( do_QueryInterface(isupports ) );
// Remove all non-editable nodes. Leave them be.
if (!mEditor->IsEditable(testNode))
{
(*outArrayOfNodes)->RemoveElementAt(i);
}
// scan for table elements. If we find table elements other than table,
// replace it with a list of any editable non-table content.
if (mEditor->IsTableElement(testNode) && !mEditor->IsTable(testNode))
{
(*outArrayOfNodes)->RemoveElementAt(i);
nsCOMPtr<nsISupportsArray> arrayOfTableContent;
res = GetTableContent(testNode, &arrayOfTableContent);
if (NS_FAILED(res)) return res;
(*outArrayOfNodes)->AppendElements(arrayOfTableContent);
}
}
return res;
}
///////////////////////////////////////////////////////////////////////////
// GetParagraphFormatNodes:
//
nsresult
nsHTMLEditRules::GetParagraphFormatNodes(nsCOMPtr<nsISupportsArray> *outArrayOfNodes,
PRBool aDontTouchContent)
{
if (!outArrayOfNodes) return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsIDOMSelection>selection;
nsresult res = mEditor->GetSelection(getter_AddRefs(selection));
if (NS_FAILED(res)) return res;
nsCOMPtr<nsISupportsArray> arrayOfRanges;
res = GetPromotedRanges(selection, &arrayOfRanges, kMakeList);
if (NS_FAILED(res)) return res;
// use these ranges to contruct a list of nodes to act on.
res = GetNodesForOperation(arrayOfRanges, outArrayOfNodes, kMakeBasicBlock, aDontTouchContent);
if (NS_FAILED(res)) return res;
// pre process our list of nodes...
@ -3285,7 +3547,7 @@ nsHTMLEditRules::ShouldMakeEmptyBlock(nsIDOMSelection *aSelection,
// Note that if _nothing_ should happen, ie, the selection is
// already entireyl inside a block (or blocks) or the correct type,
// then you don't want to return true in outMakeEmpty, since the
// defualt code will insert a new empty block anyway, rather than
// default code will insert a new empty block anyway, rather than
// doing nothing. So we have to detect that case and return false.
if (!aSelection || !outMakeEmpty) return NS_ERROR_NULL_POINTER;
@ -3342,18 +3604,7 @@ nsHTMLEditRules::ShouldMakeEmptyBlock(nsIDOMSelection *aSelection,
*outMakeEmpty = PR_FALSE;
return res;
}
// in an empty block?
PRBool bIsEmptyNode;
// the PR_TRUE param tell IsEmptyNode to not count moz-BRs as content
res = IsEmptyNode(block, &bIsEmptyNode, PR_TRUE, PR_FALSE);
if (bIsEmptyNode)
{
// we must be in a text or inline node - convert existing block
*outMakeEmpty = PR_TRUE;
return res;
}
// is it after a <br> with no inline nodes after it, or a <br> after it??
if (offset)
{
@ -3383,7 +3634,7 @@ nsHTMLEditRules::ShouldMakeEmptyBlock(nsIDOMSelection *aSelection,
// we are after a <br> and not before inline content,
// or we are between <br>s.
// make an empty block
*outMakeEmpty = PR_FALSE;
*outMakeEmpty = PR_TRUE;
return res;
}
}

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

@ -55,6 +55,7 @@ public:
// nsIHTMLEditRules methods
NS_IMETHOD GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL);
NS_IMETHOD GetIndentState(PRBool &aCanIndent, PRBool &aCanOutdent);
NS_IMETHOD GetParagraphState(PRBool &aMixed, nsString &outFormat);
// nsIEditActionListener methods
@ -97,11 +98,12 @@ protected:
nsresult WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel, PRBool *aHandled);
nsresult WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::EDirection aAction,
PRBool *aCancel, PRBool *aHandled);
nsresult WillMakeList(nsIDOMSelection *aSelection, PRBool aOrderd, PRBool *aCancel, PRBool *aHandled);
nsresult WillMakeList(nsIDOMSelection *aSelection, const nsString *aListType, PRBool *aCancel, PRBool *aHandled, const nsString *aItemType=nsnull);
nsresult WillRemoveList(nsIDOMSelection *aSelection, PRBool aOrderd, PRBool *aCancel, PRBool *aHandled);
nsresult WillIndent(nsIDOMSelection *aSelection, PRBool *aCancel, PRBool *aHandled);
nsresult WillOutdent(nsIDOMSelection *aSelection, PRBool *aCancel, PRBool *aHandled);
nsresult WillAlign(nsIDOMSelection *aSelection, const nsString *alignType, PRBool *aCancel, PRBool *aHandled);
nsresult WillMakeDefListItem(nsIDOMSelection *aSelection, const nsString *aBlockType, PRBool *aCancel, PRBool *aHandled);
nsresult WillMakeBasicBlock(nsIDOMSelection *aSelection, const nsString *aBlockType, PRBool *aCancel, PRBool *aHandled);
nsresult DidMakeBasicBlock(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, nsresult aResult);
@ -115,17 +117,13 @@ protected:
nsresult ReturnInParagraph(nsIDOMSelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset, PRBool *aCancel, PRBool *aHandled);
nsresult ReturnInListItem(nsIDOMSelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset);
nsresult AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection);
nsresult ConvertListType(nsIDOMNode *aList, nsCOMPtr<nsIDOMNode> *outList, const nsString& aListType, const nsString& aItemType);
nsresult CreateStyleForInsertText(nsIDOMSelection *aSelection, nsIDOMDocument *aDoc);
nsresult IsEmptyBlock(nsIDOMNode *aNode,
PRBool *outIsEmptyBlock,
PRBool aMozBRDoesntCount = PR_FALSE,
PRBool aListItemsNotEmpty = PR_FALSE);
#if 0
nsresult IsEmptyNode(nsIDOMNode *aNode,
PRBool *outIsEmptyBlock,
PRBool aMozBRDoesntCount = PR_FALSE,
PRBool aListItemsNotEmpty = PR_FALSE);
#endif
PRBool IsFirstNode(nsIDOMNode *aNode);
PRBool IsLastNode(nsIDOMNode *aNode);
PRBool AtStartOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aBlock);
@ -138,11 +136,13 @@ protected:
PRInt32 inOperationType);
nsresult PromoteRange(nsIDOMRange *inRange, PRInt32 inOperationType);
nsresult GetNodesForOperation(nsISupportsArray *inArrayOfRanges,
nsCOMPtr<nsISupportsArray> *outArrayOfNodes,
PRInt32 inOperationType);
nsCOMPtr<nsISupportsArray> *outArrayOfNodes,
PRInt32 inOperationType,
PRBool aDontTouchContent=PR_FALSE);
nsresult GetChildNodesForOperation(nsIDOMNode *inNode,
nsCOMPtr<nsISupportsArray> *outArrayOfNodes);
nsresult GetListActionNodes(nsCOMPtr<nsISupportsArray> *outArrayOfNodes);
nsCOMPtr<nsISupportsArray> *outArrayOfNodes);
nsresult GetListActionNodes(nsCOMPtr<nsISupportsArray> *outArrayOfNodes, PRBool aDontTouchContent=PR_FALSE);
nsresult GetParagraphFormatNodes(nsCOMPtr<nsISupportsArray> *outArrayOfNodes, PRBool aDontTouchContent=PR_FALSE);
nsresult BustUpInlinesAtBRs(nsIDOMNode *inNode,
nsCOMPtr<nsISupportsArray> *outArrayOfNodes);
nsresult MakeTransitionList(nsISupportsArray *inArrayOfNodes,
@ -185,7 +185,6 @@ protected:
nsCOMPtr<nsIDOMRange> mDocChangeRange;
PRBool mListenerEnabled;
nsCOMPtr<nsIDOMRange> mUtilRange;
nsCOMPtr<nsIDOMNode> mBody;
PRUint32 mJoinOffset; // need to remember an int across willJoin/didJoin...
};

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

@ -220,7 +220,9 @@ nsHTMLEditUtils::IsListItem(nsIDOMNode *node)
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if (tag.EqualsWithConversion("li"))
if (tag.EqualsWithConversion("li") ||
tag.EqualsWithConversion("dd") ||
tag.EqualsWithConversion("dt"))
{
return PR_TRUE;
}
@ -289,7 +291,8 @@ nsHTMLEditUtils::IsList(nsIDOMNode *node)
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if ( (tag.EqualsWithConversion("ol")) ||
if ( (tag.EqualsWithConversion("dl")) ||
(tag.EqualsWithConversion("ol")) ||
(tag.EqualsWithConversion("ul")) )
{
return PR_TRUE;
@ -299,7 +302,7 @@ nsHTMLEditUtils::IsList(nsIDOMNode *node)
///////////////////////////////////////////////////////////////////////////
// IsOrderedList: true if node an html orderd list
// IsOrderedList: true if node an html ordered list
//
PRBool
nsHTMLEditUtils::IsOrderedList(nsIDOMNode *node)
@ -317,7 +320,7 @@ nsHTMLEditUtils::IsOrderedList(nsIDOMNode *node)
///////////////////////////////////////////////////////////////////////////
// IsUnorderedList: true if node an html orderd list
// IsUnorderedList: true if node an html unordered list
//
PRBool
nsHTMLEditUtils::IsUnorderedList(nsIDOMNode *node)
@ -334,6 +337,24 @@ nsHTMLEditUtils::IsUnorderedList(nsIDOMNode *node)
}
///////////////////////////////////////////////////////////////////////////
// IsDefinitionList: true if node an html definition list
//
PRBool
nsHTMLEditUtils::IsDefinitionList(nsIDOMNode *node)
{
NS_PRECONDITION(node, "null parent passed to nsHTMLEditUtils::IsDefinitionList");
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if (tag.EqualsWithConversion("dl"))
{
return PR_TRUE;
}
return PR_FALSE;
}
///////////////////////////////////////////////////////////////////////////
// IsBlockquote: true if node an html blockquote node
//

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

@ -48,8 +48,9 @@ public:
static PRBool IsTableRow(nsIDOMNode *aNode);
static PRBool IsTableCell(nsIDOMNode *aNode);
static PRBool IsList(nsIDOMNode *aNode);
static PRBool IsUnorderedList(nsIDOMNode *aNode);
static PRBool IsOrderedList(nsIDOMNode *aNode);
static PRBool IsUnorderedList(nsIDOMNode *aNode);
static PRBool IsDefinitionList(nsIDOMNode *aNode);
static PRBool IsBlockquote(nsIDOMNode *aNode);
static PRBool IsPre(nsIDOMNode *aNode);
static PRBool IsAddress(nsIDOMNode *aNode);

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

@ -1001,6 +1001,10 @@ NS_IMETHODIMP nsHTMLEditor::InsertBR(nsCOMPtr<nsIDOMNode> *outBRNode)
if (!outBRNode) return NS_ERROR_NULL_POINTER;
*outBRNode = nsnull;
// calling it text insertion to trigger moz br treatment by rules
nsAutoRules beginRulesSniffing(this, kOpInsertText, nsIEditor::eNext);
nsresult res = GetSelection(getter_AddRefs(selection));
if (NS_FAILED(res)) return res;
res = selection->GetIsCollapsed(&bCollapsed);
@ -1019,6 +1023,8 @@ NS_IMETHODIMP nsHTMLEditor::InsertBR(nsCOMPtr<nsIDOMNode> *outBRNode)
if (NS_FAILED(res)) return res;
// position selection after br
res = GetNodeLocation(*outBRNode, &selNode, &selOffset);
if (NS_FAILED(res)) return res;
selection->SetHint(PR_TRUE);
res = selection->Collapse(selNode, selOffset+1);
@ -1127,22 +1133,28 @@ NS_IMETHODIMP nsHTMLEditor::SetInlineProperty(nsIAtom *aProperty,
if (NS_FAILED(res)) return res;
// iterate range and build up array
iter->Init(range);
while (NS_ENUMERATOR_FALSE == iter->IsDone())
res = iter->Init(range);
// init returns an error if no nodes in range.
// this can easily happen with the subtree
// iterator if the selection doesn't contain
// any *whole* nodes.
if (NS_SUCCEEDED(res))
{
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);
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;
if (IsEditable(node))
{
isupports = do_QueryInterface(node);
arrayOfNodes->AppendElement(isupports);
}
res = iter->Next();
if (NS_FAILED(res)) return res;
}
res = iter->Next();
if (NS_FAILED(res)) return res;
}
// MOOSE: workaround for selection bug:
//selection->ClearSelection();
@ -1713,7 +1725,20 @@ PRBool nsHTMLEditor::IsAtEndOfNode(nsIDOMNode *aNode, PRInt32 aOffset)
NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty,
const nsString *aAttribute,
const nsString *aValue,
PRBool &aFirst, PRBool &aAny, PRBool &aAll)
PRBool &aFirst,
PRBool &aAny,
PRBool &aAll)
{
return GetInlinePropertyWithAttrValue( aProperty, aAttribute, aValue, aFirst, aAny, aAll, nsnull);
}
NS_IMETHODIMP nsHTMLEditor::GetInlinePropertyWithAttrValue(nsIAtom *aProperty,
const nsString *aAttribute,
const nsString *aValue,
PRBool &aFirst,
PRBool &aAny,
PRBool &aAll,
nsString *outValue)
{
if (!aProperty)
return NS_ERROR_NULL_POINTER;
@ -1805,11 +1830,6 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty,
}
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
@ -1822,6 +1842,7 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty,
iter->Init(range);
nsCOMPtr<nsIContent> content;
nsAutoString firstValue, theValue;
iter->CurrentNode(getter_AddRefs(content));
while (NS_ENUMERATOR_FALSE == iter->IsDone())
{
@ -1866,12 +1887,20 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty,
{
PRBool isSet;
nsCOMPtr<nsIDOMNode>resultNode;
IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet, getter_AddRefs(resultNode));
if (first)
{
IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet, getter_AddRefs(resultNode), &firstValue);
aFirst = isSet;
first = PR_FALSE;
if (outValue) *outValue = firstValue;
}
else
{
IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet, getter_AddRefs(resultNode), &theValue);
if (firstValue != theValue)
aAll = PR_FALSE;
}
if (isSet) {
aAny = PR_TRUE;
}
@ -2790,7 +2819,10 @@ nsHTMLEditor::SetParagraphFormat(const nsString& aParagraphFormat)
{
nsAutoString tag; tag.Assign(aParagraphFormat);
tag.ToLowerCase();
return InsertBasicBlock(tag);
if (tag.EqualsWithConversion("dd") || tag.EqualsWithConversion("dt"))
return MakeDefinitionItem(tag);
else
return InsertBasicBlock(tag);
}
// XXX: ERROR_HANDLING -- this method needs a little work to ensure all error codes are
@ -2907,20 +2939,52 @@ nsHTMLEditor::GetParentBlockTags(nsStringArray *aTagList, PRBool aGetLists)
}
// get the paragraph style(s) for the selection
NS_IMETHODIMP
nsHTMLEditor::GetParagraphTags(nsStringArray *aTagList)
nsHTMLEditor::GetParagraphState(PRBool &aMixed, nsString &outFormat)
{
#if 0
if (gNoisy) { printf("---------- nsHTMLEditor::GetPargraphTags ----------\n"); }
#endif
return GetParentBlockTags(aTagList, PR_FALSE);
if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
nsCOMPtr<nsIHTMLEditRules> htmlRules = do_QueryInterface(mRules);
if (!htmlRules) return NS_ERROR_FAILURE;
return htmlRules->GetParagraphState(aMixed, outFormat);
}
NS_IMETHODIMP
nsHTMLEditor::GetListTags(nsStringArray *aTagList)
nsHTMLEditor::GetFontFaceState(PRBool &aMixed, nsString &outFace)
{
return GetParentBlockTags(aTagList, PR_TRUE);
aMixed = PR_TRUE;
outFace.AssignWithConversion("");
nsresult res;
nsAutoString faceStr; faceStr.AssignWithConversion("face");
PRBool first, any, all;
res = GetInlinePropertyWithAttrValue(nsIEditProperty::font, &faceStr, nsnull, first, any, all, &outFace);
if (NS_FAILED(res)) return res;
if (any && !all) return res; // mixed
if (all)
{
aMixed = PR_FALSE;
return res;
}
res = GetInlineProperty(nsIEditProperty::tt, nsnull, nsnull, first, any, all);
if (NS_FAILED(res)) return res;
if (any && !all) return res; // mixed
if (all)
{
aMixed = PR_FALSE;
nsIEditProperty::tt->ToString(outFace);
}
if (!any)
{
// there was no font face attrs of any kind. We are in normal font.
outFace.AssignWithConversion("");
aMixed = PR_FALSE;
}
return res;
}
NS_IMETHODIMP
@ -2934,6 +2998,17 @@ nsHTMLEditor::GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL)
return htmlRules->GetListState(aMixed, aOL, aUL);
}
NS_IMETHODIMP
nsHTMLEditor::GetIndentState(PRBool &aCanIndent, PRBool &aCanOutdent)
{
if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
nsCOMPtr<nsIHTMLEditRules> htmlRules = do_QueryInterface(mRules);
if (!htmlRules) return NS_ERROR_FAILURE;
return htmlRules->GetIndentState(aCanIndent, aCanOutdent);
}
NS_IMETHODIMP
nsHTMLEditor::MakeOrChangeList(const nsString& aListType)
{
@ -2952,8 +3027,7 @@ nsHTMLEditor::MakeOrChangeList(const nsString& aListType)
if (!selection) return NS_ERROR_NULL_POINTER;
nsTextRulesInfo ruleInfo(nsTextEditRules::kMakeList);
if (aListType.EqualsWithConversion("ol")) ruleInfo.bOrdered = PR_TRUE;
else ruleInfo.bOrdered = PR_FALSE;
ruleInfo.blockType = &aListType;
res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
if (cancel || (NS_FAILED(res))) return res;
@ -3041,8 +3115,37 @@ nsHTMLEditor::RemoveList(const nsString& aListType)
return res;
}
nsresult
nsHTMLEditor::MakeDefinitionItem(const nsString& aItemType)
{
nsresult res;
if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
NS_IMETHODIMP
nsCOMPtr<nsIDOMSelection> selection;
PRBool cancel, handled;
nsAutoEditBatch beginBatching(this);
nsAutoRules beginRulesSniffing(this, kOpMakeDefListItem, nsIEditor::eNext);
// pre-process
res = GetSelection(getter_AddRefs(selection));
if (NS_FAILED(res)) return res;
if (!selection) return NS_ERROR_NULL_POINTER;
nsTextRulesInfo ruleInfo(nsTextEditRules::kMakeDefListItem);
ruleInfo.blockType = &aItemType;
res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
if (cancel || (NS_FAILED(res))) return res;
if (!handled)
{
// todo: no default for now. we count on rules to handle it.
}
res = mRules->DidDoAction(selection, &ruleInfo, res);
return res;
}
nsresult
nsHTMLEditor::InsertBasicBlock(const nsString& aBlockType)
{
nsresult res;
@ -5534,7 +5637,8 @@ void nsHTMLEditor::IsTextPropertySetByContent(nsIDOMNode *aNode,
const nsString *aAttribute,
const nsString *aValue,
PRBool &aIsSet,
nsIDOMNode **aStyleNode) const
nsIDOMNode **aStyleNode,
nsString *outValue) const
{
nsresult result;
aIsSet = PR_FALSE; // must be initialized to false for code below to work
@ -5548,15 +5652,15 @@ void nsHTMLEditor::IsTextPropertySetByContent(nsIDOMNode *aNode,
element = do_QueryInterface(node);
if (element)
{
nsAutoString tag;
nsAutoString tag, value;
element->GetTagName(tag);
if (propName.EqualsIgnoreCase(tag))
{
PRBool found = PR_FALSE;
if (aAttribute && 0!=aAttribute->Length())
{
nsAutoString value;
element->GetAttribute(*aAttribute, value);
if (outValue) *outValue = value;
if (value.Length())
{
if (!aValue) {
@ -6182,12 +6286,14 @@ nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange)
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 it's collapsed set typing state
if (bCollapsed)
{
return NS_OK;
nsCOMPtr<nsIAtom> atom;
if (aSizeChange==1) atom = nsIEditProperty::big;
else atom = nsIEditProperty::small;
// manipulating text attributes on a collapsed selection only sets state for the next text insertion
return mTypeInState->SetProp(atom, nsnull, nsnull);
}
// wrap with txn batching, rules sniffing, and selection preservation code
@ -6346,6 +6452,9 @@ nsHTMLEditor::RelativeFontChangeOnTextNode( PRInt32 aSizeChange,
PRUint32 textLen;
aTextNode->GetLength(&textLen);
// -1 is a magic value meaning to the end of node
if (aEndOffset == -1) aEndOffset = textLen;
if ( (PRUint32)aEndOffset != textLen )
{
// we need to split off back of text node

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

@ -69,6 +69,7 @@ public:
kOpAlign = 3004,
kOpMakeBasicBlock = 3005,
kOpRemoveList = 3006,
kOpMakeDefListItem = 3007,
kOpInsertElement = 3008,
kOpInsertQuotation = 3009
};
@ -103,6 +104,11 @@ public:
const nsString *aAttribute,
const nsString *aValue,
PRBool &aFirst, PRBool &aAny, PRBool &aAll);
NS_IMETHOD GetInlinePropertyWithAttrValue(nsIAtom *aProperty,
const nsString *aAttribute,
const nsString *aValue,
PRBool &aFirst, PRBool &aAny, PRBool &aAll,
nsString *outValue);
NS_IMETHOD RemoveAllInlineProperties();
NS_IMETHOD RemoveInlineProperty(nsIAtom *aProperty, const nsString *aAttribute);
@ -125,13 +131,14 @@ public:
NS_IMETHOD SetParagraphFormat(const nsString& aParagraphFormat);
NS_IMETHOD GetParentBlockTags(nsStringArray *aTagList, PRBool aGetLists);
NS_IMETHOD GetParagraphTags(nsStringArray *aTagList);
NS_IMETHOD GetListTags(nsStringArray *aTagList);
NS_IMETHOD GetParagraphState(PRBool &aMixed, nsString &outFormat);
NS_IMETHOD GetFontFaceState(PRBool &aMixed, nsString &outFace);
NS_IMETHOD GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL);
NS_IMETHOD GetIndentState(PRBool &aCanIndent, PRBool &aCanOutdent);
NS_IMETHOD MakeOrChangeList(const nsString& aListType);
NS_IMETHOD RemoveList(const nsString& aListType);
NS_IMETHOD InsertBasicBlock(const nsString& aBlockType);
NS_IMETHOD Indent(const nsString& aIndent);
NS_IMETHOD Align(const nsString& aAlign);
@ -441,7 +448,8 @@ protected:
const nsString *aAttribute,
const nsString *aValue,
PRBool &aIsSet,
nsIDOMNode **aStyleNode) const;
nsIDOMNode **aStyleNode,
nsString *outValue = nsnull) const;
/** style-based query returns PR_TRUE if (aProperty, aAttribute) is set in aSC.
* WARNING: not well tested yet since we don't do style-based queries anywhere.
@ -473,6 +481,10 @@ protected:
/* small utility routine to test the eEditorReadonly bit */
PRBool IsModifiable();
/* helpers for block transformations */
nsresult MakeDefinitionItem(const nsString& aItemType);
nsresult InsertBasicBlock(const nsString& aBlockType);
/* increase/decrease the font size of selection */
nsresult RelativeFontChange( PRInt32 aSizeChange);

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

@ -70,6 +70,9 @@ NS_NewTextEditRules(nsIEditRules** aInstancePtrResult)
nsTextEditRules::nsTextEditRules()
: mEditor(nsnull)
, mFlags(0) // initialized to 0 ("no flags set"). Real initial value is given in Init()
, mPasswordText()
, mBogusNode(nsnull)
, mBody(nsnull)
, mActionNesting(0)
, mLockRulesSniffing(PR_FALSE)
, mTheAction(0)
@ -106,29 +109,33 @@ nsTextEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags)
nsCOMPtr<nsIDOMSelection> selection;
mEditor->GetSelection(getter_AddRefs(selection));
NS_ASSERTION(selection, "editor cannot get selection");
nsresult res = CreateBogusNodeIfNeeded(selection); // this method handles null selection, which should never happen anyway
// create a range that is the entire body contents
if (NS_FAILED(res)) return res;
// remember our root node
nsCOMPtr<nsIDOMElement> bodyElement;
res = mEditor->GetRootElement(getter_AddRefs(bodyElement));
nsresult res = mEditor->GetRootElement(getter_AddRefs(bodyElement));
if (NS_FAILED(res)) return res;
if (!bodyElement) return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsIDOMNode>bodyNode = do_QueryInterface(bodyElement);
if (!bodyNode) return NS_ERROR_FAILURE;
mBody = do_QueryInterface(bodyElement);
if (!mBody) return NS_ERROR_FAILURE;
// put in a magic br if needed
res = CreateBogusNodeIfNeeded(selection); // this method handles null selection, which should never happen anyway
if (NS_FAILED(res)) return res;
// create a range that is the entire body contents
nsCOMPtr<nsIDOMRange> wholeDoc;
res = nsComponentManager::CreateInstance(kRangeCID, nsnull, NS_GET_IID(nsIDOMRange),
getter_AddRefs(wholeDoc));
if (NS_FAILED(res)) return res;
wholeDoc->SetStart(bodyNode,0);
wholeDoc->SetStart(mBody,0);
nsCOMPtr<nsIDOMNodeList> list;
res = bodyNode->GetChildNodes(getter_AddRefs(list));
res = mBody->GetChildNodes(getter_AddRefs(list));
if (NS_FAILED(res) || !list) return res?res:NS_ERROR_FAILURE;
PRUint32 listCount;
res = list->GetLength(&listCount);
if (NS_FAILED(res)) return res;
res = wholeDoc->SetEnd(bodyNode,listCount);
res = wholeDoc->SetEnd(mBody,listCount);
if (NS_FAILED(res)) return res;
// replace newlines in that range with breaks
@ -1188,19 +1195,14 @@ nsTextEditRules::CreateBogusNodeIfNeeded(nsIDOMSelection *aSelection)
// tell rules system to not do any post-processing
nsAutoRules beginRulesSniffing(mEditor, nsEditor::kOpIgnore, nsIEditor::eNone);
nsCOMPtr<nsIDOMElement> bodyElement;
nsresult res = mEditor->GetRootElement(getter_AddRefs(bodyElement));
if (NS_FAILED(res)) return res;
if (!bodyElement) return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsIDOMNode>bodyNode = do_QueryInterface(bodyElement);
if (!mBody) return NS_ERROR_NULL_POINTER;
// now we've got the body tag.
// iterate the body tag, looking for editable content
// if no editable content is found, insert the bogus node
PRBool needsBogusContent=PR_TRUE;
nsCOMPtr<nsIDOMNode>bodyChild;
res = bodyNode->GetFirstChild(getter_AddRefs(bodyChild));
nsresult res = mBody->GetFirstChild(getter_AddRefs(bodyChild));
while ((NS_SUCCEEDED(res)) && bodyChild)
{
if (mEditor->IsMozEditorBogusNode(bodyChild) || mEditor->IsEditable(bodyChild))
@ -1214,25 +1216,27 @@ nsTextEditRules::CreateBogusNodeIfNeeded(nsIDOMSelection *aSelection)
}
if (needsBogusContent)
{
// set mBogusNode to be the newly created <br>
res = mEditor->CreateNode(NS_ConvertASCIItoUCS2("br"), bodyNode, 0,
getter_AddRefs(mBogusNode));
// create a br
nsCOMPtr<nsIDOMDocument> domDoc;
res = mEditor->GetDocument(getter_AddRefs(domDoc));
nsCOMPtr<nsIDOMElement>brElement;
res = domDoc->CreateElement(NS_ConvertASCIItoUCS2("br"),getter_AddRefs(brElement));
if (NS_FAILED(res)) return res;
// set mBogusNode to be the newly created <br>
mBogusNode = do_QueryInterface(brElement);
if (!mBogusNode) return NS_ERROR_NULL_POINTER;
// give it a special attribute
nsCOMPtr<nsIDOMElement>newPElement;
newPElement = do_QueryInterface(mBogusNode);
if (newPElement)
{
newPElement->SetAttribute(
NS_ConvertASCIItoUCS2(nsEditor::kMOZEditorBogusNodeAttr),
NS_ConvertASCIItoUCS2(nsEditor::kMOZEditorBogusNodeValue)
);
}
brElement->SetAttribute( NS_ConvertASCIItoUCS2(nsEditor::kMOZEditorBogusNodeAttr),
NS_ConvertASCIItoUCS2(nsEditor::kMOZEditorBogusNodeValue) );
// put the node in the document
res = mEditor->InsertNode(mBogusNode,mBody,0);
if (NS_FAILED(res)) return res;
// set selection
aSelection->Collapse(bodyNode,0);
aSelection->Collapse(mBody,0);
}
return res;
}

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

@ -81,6 +81,7 @@ public:
kAlign = 3004,
kMakeBasicBlock = 3005,
kRemoveList = 3006,
kMakeDefListItem = 3007,
kInsertElement = 3008
};
@ -173,6 +174,7 @@ protected:
nsHTMLEditor *mEditor; // note that we do not refcount the editor
nsString mPasswordText; // a buffer we use to store the real value of password editors
nsCOMPtr<nsIDOMNode> mBogusNode; // magic node acts as placeholder in empty doc
nsCOMPtr<nsIDOMNode> mBody; // cached root node
PRUint32 mFlags;
PRUint32 mActionNesting;
PRBool mLockRulesSniffing;
@ -195,7 +197,6 @@ class nsTextRulesInfo : public nsRulesInfo
outputFormat(0),
maxLength(-1),
collapsedAction(nsIEditor::eNext),
bOrdered(PR_FALSE),
alignType(0),
blockType(0),
insertElement(0)

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

@ -165,40 +165,6 @@ nsBaseStateUpdatingCommand::UpdateCommandState(const PRUnichar *aCommandName, ns
}
#ifdef XP_MAC
#pragma mark -
#endif
NS_IMETHODIMP
nsAlwaysEnabledCommands::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refCon, PRBool *outCmdEnabled)
{
nsCOMPtr<nsIEditorShell> editorShell = do_QueryInterface(refCon);
*outCmdEnabled = (editorShell.get() != nsnull); // enabled if we have an editorShell
return NS_OK;
}
NS_IMETHODIMP
nsAlwaysEnabledCommands::DoCommand(const PRUnichar *aCommand, nsISupports * refCon)
{
nsCOMPtr<nsIEditorShell> editorShell = do_QueryInterface(refCon);
if (!editorShell) return NS_ERROR_NULL_POINTER;
nsresult rv = NS_OK;
nsAutoString cmdString(aCommand);
/*
if (cmdString.EqualsWithConversion("cmd_scrollTop"))
return selCont->CompleteScroll(PR_FALSE);
else if (cmdString.EqualsWithConversion("cmd_scrollBottom"))
return selCont->CompleteScroll(PR_TRUE);
*/
return rv;
}
#ifdef XP_MAC
#pragma mark -
#endif
@ -522,13 +488,13 @@ nsOutdentCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refC
{
nsCOMPtr<nsIEditor> editor;
editorShell->GetEditor(getter_AddRefs(editor));
if (editor)
nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(editor);
if (htmlEditor)
{
// XXX fix me. You can't outdent if you're already at the top level.
//PRBool canOutdent;
//editor->CanIndent("outdent", &canOutdent);
PRBool canIndent, canOutdent;
htmlEditor->GetIndentState(canIndent, canOutdent);
*outCmdEnabled = PR_TRUE;
*outCmdEnabled = canOutdent;
}
}
@ -555,8 +521,21 @@ nsOutdentCommand::DoCommand(const PRUnichar *aCommand, nsISupports * refCon)
#pragma mark -
#endif
nsMultiStateCommand::nsMultiStateCommand()
: nsBaseComposerCommand()
, mGotState(PR_FALSE)
{
}
nsMultiStateCommand::~nsMultiStateCommand()
{
}
NS_IMPL_ISUPPORTS_INHERITED1(nsMultiStateCommand, nsBaseComposerCommand, nsIStateUpdatingControllerCommand);
NS_IMETHODIMP
nsParagraphStateCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refCon, PRBool *outCmdEnabled)
nsMultiStateCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports * refCon, PRBool *outCmdEnabled)
{
nsCOMPtr<nsIEditorShell> editorShell = do_QueryInterface(refCon);
*outCmdEnabled = PR_FALSE;
@ -566,34 +545,166 @@ nsParagraphStateCommand::IsCommandEnabled(const PRUnichar *aCommand, nsISupports
editorShell->GetEditor(getter_AddRefs(editor));
if (editor)
{
// should be disabled sometimes, like if the current selection is an image
*outCmdEnabled = PR_TRUE;
}
}
return NS_OK;
return UpdateCommandState(aCommand, refCon);
}
NS_IMETHODIMP
nsParagraphStateCommand::DoCommand(const PRUnichar *aCommand, nsISupports * refCon)
nsMultiStateCommand::DoCommand(const PRUnichar *aCommand, nsISupports * refCon)
{
nsCOMPtr<nsIEditorShell> editorShell = do_QueryInterface(refCon);
nsresult rv = NS_OK;
if (editorShell)
{
// we have to grab the state attribute on our command node to find out
// what format to set the paragraph to
nsAutoString stateAttribute;
rv = GetCommandNodeState(aCommand, editorShell, stateAttribute);
if (NS_FAILED(rv)) return rv;
rv = editorShell->SetParagraphFormat(stateAttribute.GetUnicode());
// we have to grab the state attribute on our command node to find out
// what format to set the paragraph to
nsAutoString stateAttribute;
rv = GetCommandNodeState(aCommand, editorShell, stateAttribute);
if (NS_FAILED(rv)) return rv;
rv = SetState(editorShell, stateAttribute);
}
return rv;
}
NS_IMETHODIMP
nsMultiStateCommand::UpdateCommandState(const PRUnichar *aCommandName, nsISupports * refCon)
{
nsCOMPtr<nsIEditorShell> editorShell = do_QueryInterface(refCon);
nsresult rv = NS_OK;
if (editorShell)
{
nsString curFormat;
PRBool isMixed;
rv = GetCurrentState(editorShell, curFormat, isMixed);
if (NS_FAILED(rv)) return rv;
if (isMixed)
curFormat.AssignWithConversion("mixed");
if (!mGotState || (curFormat != mStateString))
{
// poke the UI
SetCommandNodeState(aCommandName, editorShell, curFormat);
mGotState = PR_TRUE;
mStateString = curFormat;
}
}
return rv;
}
#ifdef XP_MAC
#pragma mark -
#endif
nsParagraphStateCommand::nsParagraphStateCommand()
: nsMultiStateCommand()
{
}
nsresult
nsParagraphStateCommand::GetCurrentState(nsIEditorShell *aEditorShell, nsString& outStateString, PRBool& outMixed)
{
NS_ASSERTION(aEditorShell, "Need an editor shell here");
nsCOMPtr<nsIEditor> editor;
aEditorShell->GetEditor(getter_AddRefs(editor));
nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(editor);
if (!htmlEditor) return NS_ERROR_FAILURE;
return htmlEditor->GetParagraphState(outMixed, outStateString);
}
nsresult
nsParagraphStateCommand::SetState(nsIEditorShell *aEditorShell, nsString& newState)
{
NS_ASSERTION(aEditorShell, "Need an editor shell here");
nsCOMPtr<nsIEditor> editor;
aEditorShell->GetEditor(getter_AddRefs(editor));
nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(editor);
if (!htmlEditor) return NS_ERROR_FAILURE;
return htmlEditor->SetParagraphFormat(newState);
}
#ifdef XP_MAC
#pragma mark -
#endif
nsFontFaceStateCommand::nsFontFaceStateCommand()
: nsMultiStateCommand()
{
}
nsresult
nsFontFaceStateCommand::GetCurrentState(nsIEditorShell *aEditorShell, nsString& outStateString, PRBool& outMixed)
{
NS_ASSERTION(aEditorShell, "Need an editor shell here");
nsCOMPtr<nsIEditor> editor;
aEditorShell->GetEditor(getter_AddRefs(editor));
nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(editor);
if (!htmlEditor) return NS_ERROR_FAILURE;
return htmlEditor->GetFontFaceState(outMixed, outStateString);
}
nsresult
nsFontFaceStateCommand::SetState(nsIEditorShell *aEditorShell, nsString& newState)
{
NS_ASSERTION(aEditorShell, "Need an editor shell here");
nsCOMPtr<nsIEditor> editor;
aEditorShell->GetEditor(getter_AddRefs(editor));
nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(editor);
if (!htmlEditor) return NS_ERROR_FAILURE;
nsresult rv;
NS_ConvertASCIItoUCS2 emptyString("");
NS_ConvertASCIItoUCS2 fontString("font");
NS_ConvertASCIItoUCS2 faceString("face");
nsCOMPtr<nsIAtom> ttAtom = getter_AddRefs(NS_NewAtom("tt"));
nsCOMPtr<nsIAtom> fontAtom = getter_AddRefs(NS_NewAtom("font"));
if (newState.EqualsWithConversion("tt"))
{
// The old "teletype" attribute
rv = htmlEditor->SetInlineProperty(ttAtom, &emptyString, &emptyString);
// Clear existing font face
rv = htmlEditor->RemoveInlineProperty(fontAtom, &faceString);
}
else
{
// Remove any existing TT nodes
rv = htmlEditor->RemoveInlineProperty(ttAtom, &emptyString);
if (newState == emptyString || newState.EqualsWithConversion("normal")) {
rv = htmlEditor->RemoveInlineProperty(fontAtom, &faceString);
} else {
rv = htmlEditor->SetInlineProperty(fontAtom, &faceString, &newState);
}
}
return rv;
}
#ifdef XP_MAC
#pragma mark -
#endif

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

@ -66,7 +66,7 @@ public: \
NS_DECL_NSICONTROLLERCOMMAND \
};
// virtual base class for commands that need to save and update state
// virtual base class for commands that need to save and update Boolean state (like styles etc)
class nsBaseStateUpdatingCommand : public nsBaseComposerCommand,
public nsIStateUpdatingControllerCommand
{
@ -98,7 +98,8 @@ protected:
};
// shared class for the various style updating commands
// Shared class for the various style updating commands like bold, italics etc.
// Suitable for commands whose state is either 'on' or 'off'.
class nsStyleUpdatingCommand : public nsBaseStateUpdatingCommand
{
public:
@ -132,8 +133,58 @@ protected:
};
// Base class for commands whose state consists of a string (e.g. para format)
class nsMultiStateCommand : public nsBaseComposerCommand,
public nsIStateUpdatingControllerCommand
{
public:
nsMultiStateCommand();
virtual ~nsMultiStateCommand();
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSICONTROLLERCOMMAND
NS_DECL_NSISTATEUPDATINGCONTROLLERCOMMAND
protected:
virtual nsresult GetCurrentState(nsIEditorShell *aEditorShell, nsString& outStateString, PRBool& outMixed) = 0;
virtual nsresult SetState(nsIEditorShell *aEditorShell, nsString& newState) = 0;
protected:
PRPackedBool mGotState;
nsString mStateString;
};
class nsParagraphStateCommand : public nsMultiStateCommand
{
public:
nsParagraphStateCommand();
protected:
virtual nsresult GetCurrentState(nsIEditorShell *aEditorShell, nsString& outStateString, PRBool& outMixed);
virtual nsresult SetState(nsIEditorShell *aEditorShell, nsString& newState);
};
class nsFontFaceStateCommand : public nsMultiStateCommand
{
public:
nsFontFaceStateCommand();
protected:
virtual nsresult GetCurrentState(nsIEditorShell *aEditorShell, nsString& outStateString, PRBool& outMixed);
virtual nsresult SetState(nsIEditorShell *aEditorShell, nsString& newState);
};
// composer commands
NS_DECL_COMPOSER_COMMAND(nsAlwaysEnabledCommands)
NS_DECL_COMPOSER_COMMAND(nsCloseCommand)
NS_DECL_COMPOSER_COMMAND(nsPrintingCommands)
@ -157,7 +208,6 @@ NS_DECL_COMPOSER_COMMAND(nsPasteQuotationCommand)
NS_DECL_COMPOSER_COMMAND(nsIndentCommand)
NS_DECL_COMPOSER_COMMAND(nsOutdentCommand)
NS_DECL_COMPOSER_COMMAND(nsParagraphStateCommand)
NS_DECL_COMPOSER_COMMAND(nsAlignCommand)
NS_DECL_COMPOSER_COMMAND(nsRemoveStylesCommand)
NS_DECL_COMPOSER_COMMAND(nsIncreaseFontSizeCommand)

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

@ -3695,6 +3695,23 @@ nsEditor::NodeIsType(nsIDOMNode *aNode, nsIAtom *aTag)
return PR_FALSE;
}
PRBool
nsEditor::NodeIsType(nsIDOMNode *aNode, const nsString &aTagStr)
{
nsCOMPtr<nsIDOMElement>element;
element = do_QueryInterface(aNode);
if (element)
{
nsAutoString tag;
element->GetTagName(tag);
if (tag.EqualsIgnoreCase(aTagStr))
{
return PR_TRUE;
}
}
return PR_FALSE;
}
PRBool
nsEditor::CanContainTag(nsIDOMNode* aParent, const nsString &aChildTag)
{

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

@ -598,6 +598,7 @@ public:
/** returns PR_TRUE if aNode is of the type implied by aTag */
static PRBool NodeIsType(nsIDOMNode *aNode, nsIAtom *aTag);
static PRBool NodeIsType(nsIDOMNode *aNode, const nsString &aTag);
/** returns PR_TRUE if aParent can contain a child of type aTag */
PRBool CanContainTag(nsIDOMNode* aParent, const nsString &aTag);

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

@ -327,6 +327,8 @@ nsresult nsComposerController::RegisterComposerCommands(nsIControllerCommandMana
// format stuff
NS_REGISTER_ONE_COMMAND(nsParagraphStateCommand, "cmd_paragraphState");
NS_REGISTER_ONE_COMMAND(nsFontFaceStateCommand, "cmd_fontFace");
NS_REGISTER_ONE_COMMAND(nsAlignCommand, "cmd_align");
NS_REGISTER_ONE_COMMAND(nsRemoveStylesCommand, "cmd_removeStyles");
NS_REGISTER_ONE_COMMAND(nsIncreaseFontSizeCommand, "cmd_increaseFont");

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

@ -57,6 +57,7 @@ TypeInState::QueryInterface(REFNSIID aIID, void** aInstancePtr)
TypeInState::TypeInState() :
mSetArray()
,mClearedArray()
,mRelativeFontSize(0)
{
NS_INIT_REFCNT();
Reset();
@ -108,6 +109,18 @@ nsresult TypeInState::SetProp(nsIAtom *aProp, const nsString &aAttr)
nsresult TypeInState::SetProp(nsIAtom *aProp, const nsString &aAttr, const nsString &aValue)
{
// special case for big/small, these nest
if (nsIEditProperty::big == aProp)
{
mRelativeFontSize++;
return NS_OK;
}
if (nsIEditProperty::small == aProp)
{
mRelativeFontSize--;
return NS_OK;
}
// if it's already set we are done
if (IsPropSet(aProp,aAttr,aValue)) return NS_OK;
@ -196,6 +209,17 @@ nsresult TypeInState::TakeSetProperty(PropItem **outPropItem)
return NS_OK;
}
//**************************************************************************
// TakeRelativeFontSize: hands back relative font value, which is then
// cleared out.
nsresult TypeInState::TakeRelativeFontSize(PRInt32 *outRelSize)
{
if (!outRelSize) return NS_ERROR_NULL_POINTER;
*outRelSize = mRelativeFontSize;
mRelativeFontSize = 0;
return NS_OK;
}
nsresult TypeInState::GetTypingState(PRBool &isSet, PRBool &theSetting, nsIAtom *aProp)
{
return GetTypingState(isSet, theSetting, aProp, nsAutoString(), nsAutoString());
@ -248,6 +272,7 @@ nsresult TypeInState::RemovePropFromSetList(nsIAtom *aProp,
if (!aProp)
{
// clear _all_ props
mRelativeFontSize=0;
while ((index = mSetArray.Count()))
{
// go backwards to keep nsVoidArray from memmoving everything each time

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

@ -69,6 +69,11 @@ public:
// caller assumes ownership of PropItem and must delete it.
nsresult TakeSetProperty(PropItem **outPropItem);
//**************************************************************************
// TakeRelativeFontSize: hands back relative font value, which is then
// cleared out.
nsresult TakeRelativeFontSize(PRInt32 *outRelSize);
nsresult GetTypingState(PRBool &isSet, PRBool &theSetting, nsIAtom *aProp);
nsresult GetTypingState(PRBool &isSet, PRBool &theSetting, nsIAtom *aProp,
const nsString &aAttr);
@ -87,6 +92,7 @@ protected:
nsVoidArray mSetArray;
nsVoidArray mClearedArray;
PRInt32 mRelativeFontSize;
};

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

@ -79,7 +79,6 @@ nsHTMLEditRules::nsHTMLEditRules() :
mDocChangeRange(nsnull)
,mListenerEnabled(PR_TRUE)
,mUtilRange(nsnull)
,mBody(nsnull)
,mJoinOffset(0)
{
}
@ -192,73 +191,8 @@ nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection)
nsresult res = NS_OK;
if (!--mActionNesting)
{
ConfirmSelectionInBody();
if (action == nsEditor::kOpIgnore) return NS_OK;
nsCOMPtr<nsIDOMSelection>selection;
res = mEditor->GetSelection(getter_AddRefs(selection));
if (NS_FAILED(res)) return res;
if (mDocChangeRange && !((action == nsEditor::kOpUndo) || (action == nsEditor::kOpRedo)))
{
// dont let any txns in here move the selection around behind our back.
// Note that this won't prevent explicit selection setting from working.
nsAutoTxnsConserveSelection dontSpazMySelection(mEditor);
// expand the "changed doc range" as needed
res = PromoteRange(mDocChangeRange, action);
if (NS_FAILED(res)) return res;
// add in any needed <br>s, and remove any unneeded ones.
res = AdjustSpecialBreaks();
if (NS_FAILED(res)) return res;
// merge any adjacent text nodes
if ( (action != nsEditor::kOpInsertText &&
action != nsEditor::kOpInsertIMEText) )
{
res = mEditor->CollapseAdjacentTextNodes(mDocChangeRange);
if (NS_FAILED(res)) return res;
}
// adjust whitespace for insert text and delete actions
if ((action == nsEditor::kOpInsertText) ||
(action == nsEditor::kOpInsertIMEText) ||
(action == nsEditor::kOpDeleteSelection))
{
res = AdjustWhitespace(selection);
if (NS_FAILED(res)) return res;
}
// replace newlines that are preformatted
// MOOSE: This is buttUgly. A better way to
// organize the action enum is in order.
if ((action == nsEditor::kOpInsertText) ||
(action == nsEditor::kOpInsertIMEText) ||
(action == nsHTMLEditor::kOpInsertElement) ||
(action == nsHTMLEditor::kOpInsertQuotation) ||
(action == nsEditor::kOpInsertNode))
{
res = ReplaceNewlines(mDocChangeRange);
}
// clean up any empty nodes in the selection
res = RemoveEmptyNodes();
if (NS_FAILED(res)) return res;
// adjust selection for insert text and delete actions
if ((action == nsEditor::kOpInsertText) ||
(action == nsEditor::kOpInsertIMEText) ||
(action == nsEditor::kOpDeleteSelection))
{
res = AdjustSelection(selection, aDirection);
if (NS_FAILED(res)) return res;
}
}
// detect empty doc
res = CreateBogusNodeIfNeeded(selection);
// do all the tricky stuff
res = AfterEditInner(action, aDirection);
// turn on caret
nsCOMPtr<nsISelectionController> selCon;
mEditor->GetSelectionController(getter_AddRefs(selCon));
@ -269,6 +203,79 @@ nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection)
}
nsresult
nsHTMLEditRules::AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection)
{
ConfirmSelectionInBody();
if (action == nsEditor::kOpIgnore) return NS_OK;
nsCOMPtr<nsIDOMSelection>selection;
nsresult res = mEditor->GetSelection(getter_AddRefs(selection));
if (NS_FAILED(res)) return res;
if (mDocChangeRange && !((action == nsEditor::kOpUndo) || (action == nsEditor::kOpRedo)))
{
// dont let any txns in here move the selection around behind our back.
// Note that this won't prevent explicit selection setting from working.
nsAutoTxnsConserveSelection dontSpazMySelection(mEditor);
// expand the "changed doc range" as needed
res = PromoteRange(mDocChangeRange, action);
if (NS_FAILED(res)) return res;
// add in any needed <br>s, and remove any unneeded ones.
res = AdjustSpecialBreaks();
if (NS_FAILED(res)) return res;
// merge any adjacent text nodes
if ( (action != nsEditor::kOpInsertText &&
action != nsEditor::kOpInsertIMEText) )
{
res = mEditor->CollapseAdjacentTextNodes(mDocChangeRange);
if (NS_FAILED(res)) return res;
}
// adjust whitespace for insert text and delete actions
if ((action == nsEditor::kOpInsertText) ||
(action == nsEditor::kOpInsertIMEText) ||
(action == nsEditor::kOpDeleteSelection))
{
res = AdjustWhitespace(selection);
if (NS_FAILED(res)) return res;
}
// replace newlines that are preformatted
// MOOSE: This is buttUgly. A better way to
// organize the action enum is in order.
if ((action == nsEditor::kOpInsertText) ||
(action == nsEditor::kOpInsertIMEText) ||
(action == nsHTMLEditor::kOpInsertElement) ||
(action == nsHTMLEditor::kOpInsertQuotation) ||
(action == nsEditor::kOpInsertNode))
{
res = ReplaceNewlines(mDocChangeRange);
}
// clean up any empty nodes in the selection
res = RemoveEmptyNodes();
if (NS_FAILED(res)) return res;
// adjust selection for insert text and delete actions
if ((action == nsEditor::kOpInsertText) ||
(action == nsEditor::kOpInsertIMEText) ||
(action == nsEditor::kOpDeleteSelection))
{
res = AdjustSelection(selection, aDirection);
if (NS_FAILED(res)) return res;
}
}
// detect empty doc
res = CreateBogusNodeIfNeeded(selection);
return res;
}
NS_IMETHODIMP
nsHTMLEditRules::WillDoAction(nsIDOMSelection *aSelection,
nsRulesInfo *aInfo,
@ -303,7 +310,7 @@ nsHTMLEditRules::WillDoAction(nsIDOMSelection *aSelection,
case kDeleteSelection:
return WillDeleteSelection(aSelection, info->collapsedAction, aCancel, aHandled);
case kMakeList:
return WillMakeList(aSelection, info->bOrdered, aCancel, aHandled);
return WillMakeList(aSelection, info->blockType, aCancel, aHandled);
case kIndent:
return WillIndent(aSelection, aCancel, aHandled);
case kOutdent:
@ -314,6 +321,8 @@ nsHTMLEditRules::WillDoAction(nsIDOMSelection *aSelection,
return WillMakeBasicBlock(aSelection, info->blockType, aCancel, aHandled);
case kRemoveList:
return WillRemoveList(aSelection, info->bOrdered, aCancel, aHandled);
case kMakeDefListItem:
return WillMakeDefListItem(aSelection, info->blockType, aCancel, aHandled);
case kInsertElement:
return WillInsert(aSelection, aCancel);
}
@ -349,10 +358,10 @@ nsHTMLEditRules::GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL)
PRBool bNonList = PR_FALSE;
nsCOMPtr<nsISupportsArray> arrayOfNodes;
nsresult res = GetListActionNodes(&arrayOfNodes);
nsresult res = GetListActionNodes(&arrayOfNodes, PR_TRUE);
if (NS_FAILED(res)) return res;
// yummy yummy yummy i've got nodes in my tummy
// examine list type for nodes in selection
PRUint32 listCount;
PRInt32 i;
arrayOfNodes->Count(&listCount);
@ -389,7 +398,102 @@ nsHTMLEditRules::GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL)
NS_IMETHODIMP
nsHTMLEditRules::GetIndentState(PRBool &aCanIndent, PRBool &aCanOutdent)
{
return NS_ERROR_FAILURE;
aCanIndent = PR_TRUE;
aCanOutdent = PR_FALSE;
nsCOMPtr<nsISupportsArray> arrayOfNodes;
nsresult res = GetListActionNodes(&arrayOfNodes, PR_TRUE);
if (NS_FAILED(res)) return res;
// examine nodes in selection for blockquotes or list elements;
// these we can outdent. Note that we return true for canOutdent
// if *any* of the selection is outdentable, rather than all of it.
PRUint32 listCount;
PRInt32 i;
arrayOfNodes->Count(&listCount);
for (i=(PRInt32)listCount-1; i>=0; i--)
{
nsCOMPtr<nsISupports> isupports = (dont_AddRef)(arrayOfNodes->ElementAt(i));
nsCOMPtr<nsIDOMNode> curNode( do_QueryInterface(isupports) );
if (nsHTMLEditUtils::IsList(curNode) ||
nsHTMLEditUtils::IsListItem(curNode) ||
nsHTMLEditUtils::IsBlockquote(curNode))
{
aCanOutdent = PR_TRUE;
break;
}
}
return res;
}
NS_IMETHODIMP
nsHTMLEditRules::GetParagraphState(PRBool &aMixed, nsString &outFormat)
{
// This routine is *heavily* tied to our ui choices in the paragraph
// style popup. I cant see a way around that.
aMixed = PR_TRUE;
outFormat.AssignWithConversion("");
PRBool bMixed = PR_FALSE;
nsAutoString formatStr;
// using "x" as anuninitialized value, since "" is meaningful
formatStr.AssignWithConversion("x");
nsCOMPtr<nsISupportsArray> arrayOfNodes;
nsresult res = GetParagraphFormatNodes(&arrayOfNodes, PR_TRUE);
if (NS_FAILED(res)) return res;
// loop through the nodes in selection and examine their paragraph format
PRUint32 listCount;
PRInt32 i;
arrayOfNodes->Count(&listCount);
for (i=(PRInt32)listCount-1; i>=0; i--)
{
nsCOMPtr<nsISupports> isupports = (dont_AddRef)(arrayOfNodes->ElementAt(i));
nsCOMPtr<nsIDOMNode> curNode( do_QueryInterface(isupports) );
nsAutoString format;
nsCOMPtr<nsIAtom> atom = mEditor->GetTag(curNode);
if (mEditor->IsInlineNode(curNode))
format.AssignWithConversion("");
else if (nsIEditProperty::p == atom)
format.AssignWithConversion("P");
else if (nsHTMLEditUtils::IsHeader(curNode))
{
nsAutoString tag;
nsEditor::GetTagString(curNode,tag);
tag.ToUpperCase();
format = tag;
}
else if (nsIEditProperty::blockquote == atom)
format.AssignWithConversion("BLOCKQUOTE");
else if (nsIEditProperty::address == atom)
format.AssignWithConversion("ADDRESS");
else if (nsIEditProperty::pre == atom)
format.AssignWithConversion("PRE");
else if (nsIEditProperty::dt == atom)
format.AssignWithConversion("DT");
else if (nsIEditProperty::dd == atom)
format.AssignWithConversion("DD");
// if this is the first node, we've found, remember it as the format
if (formatStr.EqualsWithConversion("x"))
formatStr = format;
// else make sure it matches previously found format
else if (format != formatStr)
{
bMixed = PR_TRUE;
break;
}
}
aMixed = bMixed;
outFormat = formatStr;
return res;
}
@ -825,19 +929,30 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection,
}
}
}
// is prior node inline and same type? (This shouldn't happen)
if ( mEditor->HasSameBlockNodeParent(node, priorNode) &&
( mEditor->IsInlineNode(node) || mEditor->IsTextNode(node) ) &&
mEditor->NodesSameType(node, priorNode) )
// is prior node a text node?
else if ( mEditor->IsTextNode(priorNode) )
{
// if so, join them!
nsCOMPtr<nsIDOMNode> topParent;
priorNode->GetParentNode(getter_AddRefs(topParent));
*aHandled = PR_TRUE;
res = JoinNodesSmart(priorNode,node,&selNode,&selOffset);
// delete last character
nsCOMPtr<nsIDOMCharacterData>nodeAsText;
nodeAsText = do_QueryInterface(priorNode);
nodeAsText->GetLength((PRUint32*)&offset);
res = aSelection->Collapse(priorNode,offset);
// just return without setting handled to true.
// default code will take care of actual deletion
return res;
}
else if ( mEditor->IsInlineNode(priorNode) )
{
// remember where we are
res = mEditor->GetNodeLocation(priorNode, &node, &offset);
if (NS_FAILED(res)) return res;
// delete it
res = mEditor->DeleteNode(priorNode);
if (NS_FAILED(res)) return res;
// we did something, so lets say so.
*aHandled = PR_TRUE;
// fix up selection
res = aSelection->Collapse(selNode,selOffset);
res = aSelection->Collapse(node,offset);
return res;
}
else return NS_OK; // punt to default
@ -924,19 +1039,30 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection,
}
}
}
// is next node inline and same type? (This shouldn't happen)
if ( mEditor->HasSameBlockNodeParent(node, nextNode) &&
( mEditor->IsInlineNode(node) || mEditor->IsTextNode(node) ) &&
mEditor->NodesSameType(node, nextNode) )
// is next node a text node?
else if ( mEditor->IsTextNode(nextNode) )
{
// if so, join them!
nsCOMPtr<nsIDOMNode> topParent;
nextNode->GetParentNode(getter_AddRefs(topParent));
*aHandled = PR_TRUE;
res = JoinNodesSmart(node,nextNode,&selNode,&selOffset);
// delete last character
nsCOMPtr<nsIDOMCharacterData>nodeAsText;
nodeAsText = do_QueryInterface(nextNode);
nodeAsText->GetLength((PRUint32*)&offset);
res = aSelection->Collapse(nextNode,offset);
// just return without setting handled to true.
// default code will take care of actual deletion
return res;
}
else if ( mEditor->IsInlineNode(nextNode) )
{
// remember where we are
res = mEditor->GetNodeLocation(nextNode, &node, &offset);
if (NS_FAILED(res)) return res;
// delete it
res = mEditor->DeleteNode(nextNode);
if (NS_FAILED(res)) return res;
// we did something, so lets say so.
*aHandled = PR_TRUE;
// fix up selection
res = aSelection->Collapse(selNode,selOffset);
res = aSelection->Collapse(node,offset);
return res;
}
else return NS_OK; // punt to default
@ -1001,6 +1127,11 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection,
if (NS_FAILED(res)) return res;
if (!nodeToDelete) return NS_ERROR_NULL_POINTER;
if (mBody == nodeToDelete)
{
*aCancel = PR_TRUE;
return res;
}
// if this node is text node, adjust selection
if (nsEditor::IsTextNode(nodeToDelete))
@ -1153,28 +1284,32 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection,
nsresult
nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection,
PRBool aOrdered,
const nsString *aListType,
PRBool *aCancel,
PRBool *aHandled)
PRBool *aHandled,
const nsString *aItemType)
{
if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
if (!aSelection || !aListType || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
nsresult res = WillInsert(aSelection, aCancel);
if (NS_FAILED(res)) return res;
nsAutoString listType;
listType.AssignWithConversion( aOrdered ? "ol" : "ul" );
// initialize out param
// we want to ignore result of WillInsert()
*aCancel = PR_FALSE;
*aHandled = PR_FALSE;
nsAutoString blockType;
blockType.AssignWithConversion( aOrdered ? "ol" : "ul");
// deduce what tag to use for list items
nsAutoString itemType;
if (aItemType)
itemType = *aItemType;
else if (aListType->EqualsWithConversion("dl"))
itemType.AssignWithConversion("dd");
else
itemType.AssignWithConversion("li");
PRBool outMakeEmpty;
res = ShouldMakeEmptyBlock(aSelection, &blockType, &outMakeEmpty);
res = ShouldMakeEmptyBlock(aSelection, aListType, &outMakeEmpty);
if (NS_FAILED(res)) return res;
if (outMakeEmpty)
{
@ -1186,11 +1321,11 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection,
if (NS_FAILED(res)) return res;
// make sure we can put a list here
res = SplitAsNeeded(&listType, &parent, &offset);
res = SplitAsNeeded(aListType, &parent, &offset);
if (NS_FAILED(res)) return res;
res = mEditor->CreateNode(listType, parent, offset, getter_AddRefs(theList));
res = mEditor->CreateNode(*aListType, parent, offset, getter_AddRefs(theList));
if (NS_FAILED(res)) return res;
res = mEditor->CreateNode(NS_ConvertASCIItoUCS2("li"), theList, 0, getter_AddRefs(theListItem));
res = mEditor->CreateNode(itemType, theList, 0, getter_AddRefs(theListItem));
if (NS_FAILED(res)) return res;
// put selection in new list item
res = aSelection->Collapse(theListItem,0);
@ -1224,8 +1359,7 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection,
nsCOMPtr<nsIDOMNode> curNode( do_QueryInterface(isupports) );
while (nsHTMLEditUtils::IsDiv(curNode)
|| nsHTMLEditUtils::IsOrderedList(curNode)
|| nsHTMLEditUtils::IsUnorderedList(curNode)
|| nsHTMLEditUtils::IsList(curNode)
|| nsHTMLEditUtils::IsBlockquote(curNode))
{
// dive as long as there is only one child, and it is a list, div, blockquote
@ -1238,8 +1372,7 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection,
// keep diving
nsCOMPtr <nsIDOMNode> tmpNode = nsEditor::GetChildAt(curNode, 0);
if (nsHTMLEditUtils::IsDiv(tmpNode)
|| nsHTMLEditUtils::IsOrderedList(tmpNode)
|| nsHTMLEditUtils::IsUnorderedList(tmpNode)
|| nsHTMLEditUtils::IsList(tmpNode)
|| nsHTMLEditUtils::IsBlockquote(tmpNode))
{
// check editablility XXX floppy moose
@ -1305,37 +1438,38 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection,
if (nsHTMLEditUtils::IsList(curNode))
{
nsAutoString existingListStr;
res = mEditor->GetTagString(curNode, existingListStr);
// do we have a curList already?
if (curList && !nsHTMLEditUtils::IsDescendantOf(curNode, curList))
{
// move all of our dachildren into curList.
// move all of our children into curList.
// cheezy way to do it: move whole list and then
// RemoveContainer() on the list
// RemoveContainer() on the list.
// ConvertListType first: that routine
// handles converting the list item types, if needed
res = mEditor->MoveNode(curNode, curList, -1);
if (NS_FAILED(res)) return res;
res = mEditor->RemoveContainer(curNode);
res = ConvertListType(curNode, &newBlock, *aListType, itemType);
if (NS_FAILED(res)) return res;
}
else if ( (nsHTMLEditUtils::IsUnorderedList(curNode) && aOrdered) ||
(nsHTMLEditUtils::IsOrderedList(curNode) && !aOrdered) )
{
// replace list with new list type
res = mEditor->ReplaceContainer(curNode,&newBlock,blockType);
res = mEditor->RemoveContainer(newBlock);
if (NS_FAILED(res)) return res;
curList = newBlock;
}
else
{
curList = curNode;
// replace list with new list type
res = ConvertListType(curNode, &newBlock, *aListType, itemType);
if (NS_FAILED(res)) return res;
curList = newBlock;
}
continue;
}
if (nsHTMLEditUtils::IsListItem(curNode))
{
if ( (nsHTMLEditUtils::IsUnorderedList(curParent) && aOrdered) ||
(nsHTMLEditUtils::IsOrderedList(curParent) && !aOrdered) )
nsAutoString existingListStr;
res = mEditor->GetTagString(curParent, existingListStr);
if ( existingListStr != *aListType )
{
// list item is in wrong type of list.
// if we dont have a curList, split the old list
@ -1348,16 +1482,23 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection,
PRInt32 o;
res = nsEditor::GetNodeLocation(curParent, &p, &o);
if (NS_FAILED(res)) return res;
res = mEditor->CreateNode(listType, p, o, getter_AddRefs(curList));
res = mEditor->CreateNode(*aListType, p, o, getter_AddRefs(curList));
if (NS_FAILED(res)) return res;
}
// move list item to new list
res = mEditor->MoveNode(curNode, curList, -1);
if (NS_FAILED(res)) return res;
// convert list item type if needed
if (!mEditor->NodeIsType(curNode,itemType))
{
res = mEditor->ReplaceContainer(curNode, &newBlock, itemType);
if (NS_FAILED(res)) return res;
}
}
else
{
// item is in right type of list. But we might still have to move it.
// and we might need to convert list item types.
if (!curList)
curList = curParent;
else
@ -1369,6 +1510,11 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection,
if (NS_FAILED(res)) return res;
}
}
if (!mEditor->NodeIsType(curNode,itemType))
{
res = mEditor->ReplaceContainer(curNode, &newBlock, itemType);
if (NS_FAILED(res)) return res;
}
}
continue;
}
@ -1377,9 +1523,9 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection,
// or if this node doesn't go in list we used earlier.
if (!curList) // || transitionList[i])
{
res = SplitAsNeeded(&listType, &curParent, &offset);
res = SplitAsNeeded(aListType, &curParent, &offset);
if (NS_FAILED(res)) return res;
res = mEditor->CreateNode(listType, curParent, offset, getter_AddRefs(curList));
res = mEditor->CreateNode(*aListType, curParent, offset, getter_AddRefs(curList));
if (NS_FAILED(res)) return res;
// curList is now the correct thing to put curNode in
prevListItem = 0;
@ -1404,11 +1550,11 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection,
// don't wrap li around a paragraph. instead replace paragraph with li
if (nsHTMLEditUtils::IsParagraph(curNode))
{
res = mEditor->ReplaceContainer(curNode, &listItem, NS_ConvertASCIItoUCS2("li"));
res = mEditor->ReplaceContainer(curNode, &listItem, itemType);
}
else
{
res = mEditor->InsertContainerAbove(curNode, &listItem, NS_ConvertASCIItoUCS2("li"));
res = mEditor->InsertContainerAbove(curNode, &listItem, itemType);
}
if (NS_FAILED(res)) return res;
if (nsEditor::IsInlineNode(curNode))
@ -1525,6 +1671,18 @@ nsHTMLEditRules::WillRemoveList(nsIDOMSelection *aSelection,
}
nsresult
nsHTMLEditRules::WillMakeDefListItem(nsIDOMSelection *aSelection,
const nsString *aItemType,
PRBool *aCancel,
PRBool *aHandled)
{
// for now we let WillMakeList handle this
nsAutoString listType;
listType.AssignWithConversion("dl");
return WillMakeList(aSelection, &listType, aCancel, aHandled, aItemType);
}
nsresult
nsHTMLEditRules::WillMakeBasicBlock(nsIDOMSelection *aSelection,
const nsString *aBlockType,
@ -1808,6 +1966,40 @@ nsHTMLEditRules::WillOutdent(nsIDOMSelection *aSelection, PRBool *aCancel, PRBoo
}
///////////////////////////////////////////////////////////////////////////
// ConvertListType: convert list type and list item type.
//
//
nsresult
nsHTMLEditRules::ConvertListType(nsIDOMNode *aList,
nsCOMPtr<nsIDOMNode> *outList,
const nsString& aListType,
const nsString& aItemType)
{
if (!aList || !outList) return NS_ERROR_NULL_POINTER;
*outList = aList; // we migvht not need to change the list
nsresult res = NS_OK;
nsCOMPtr<nsIDOMNode> child, temp;
aList->GetFirstChild(getter_AddRefs(child));
while (child)
{
if (!mEditor->NodeIsType(child, aItemType))
{
res = mEditor->ReplaceContainer(child, &temp, aItemType);
if (NS_FAILED(res)) return res;
child = temp;
}
child->GetNextSibling(getter_AddRefs(temp));
child = temp;
}
if (!mEditor->NodeIsType(aList, aListType))
{
res = mEditor->ReplaceContainer(aList, outList, aListType);
}
return res;
}
///////////////////////////////////////////////////////////////////////////
// CreateStyleForInsertText: take care of clearing and setting appropriate
// style nodes for text insertion.
@ -1839,10 +2031,15 @@ nsHTMLEditRules::CreateStyleForInsertText(nsIDOMSelection *aSelection, nsIDOMDoc
}
// then process setting any styles
mEditor->mTypeInState->TakeSetProperty(&item);
PRInt32 relFontSize;
if (item) // we have at least one style to add; make a
{ // new text node to insert style nodes above.
res = mEditor->mTypeInState->TakeRelativeFontSize(&relFontSize);
if (NS_FAILED(res)) return res;
res = mEditor->mTypeInState->TakeSetProperty(&item);
if (NS_FAILED(res)) return res;
if (item || relFontSize) // we have at least one style to add; make a
{ // new text node to insert style nodes above.
if (mEditor->IsTextNode(node))
{
// if we are in a text node, split it
@ -1863,17 +2060,29 @@ nsHTMLEditRules::CreateStyleForInsertText(nsIDOMSelection *aSelection, nsIDOMDoc
node = newNode;
offset = 0;
weDidSometing = PR_TRUE;
if (relFontSize)
{
PRInt32 j, dir;
// dir indicated bigger versus smaller. 1 = bigger, -1 = smaller
if (relFontSize > 0) dir=1;
else dir = -1;
for (j=0; j<abs(relFontSize); j++)
{
res = mEditor->RelativeFontChangeOnTextNode(dir, nodeAsText, 0, -1);
if (NS_FAILED(res)) return res;
}
}
while (item)
{
res = mEditor->SetInlinePropertyOnNode(node, item->tag, &item->attr, &item->value);
if (NS_FAILED(res)) return res;
// we own item now (TakeSetProperty hands ownership to us)
delete item;
mEditor->mTypeInState->TakeSetProperty(&item);
}
}
while (item)
{
res = mEditor->SetInlinePropertyOnNode(node, item->tag, &item->attr, &item->value);
if (NS_FAILED(res)) return res;
// we own item now (TakeSetProperty hands ownership to us)
delete item;
mEditor->mTypeInState->TakeSetProperty(&item);
}
if (weDidSometing)
return aSelection->Collapse(node, offset);
@ -2616,7 +2825,8 @@ nsHTMLEditRules::PromoteRange(nsIDOMRange *inRange,
nsresult
nsHTMLEditRules::GetNodesForOperation(nsISupportsArray *inArrayOfRanges,
nsCOMPtr<nsISupportsArray> *outArrayOfNodes,
PRInt32 inOperationType)
PRInt32 inOperationType,
PRBool aDontTouchContent)
{
if (!inArrayOfRanges || !outArrayOfNodes) return NS_ERROR_NULL_POINTER;
@ -2669,7 +2879,7 @@ nsHTMLEditRules::GetNodesForOperation(nsISupportsArray *inArrayOfRanges,
{
nsCOMPtr<nsISupports> isupports = (dont_AddRef)((*outArrayOfNodes)->ElementAt(i));
nsCOMPtr<nsIDOMNode> node( do_QueryInterface(isupports) );
if (mEditor->IsInlineNode(node) && mEditor->IsContainer(node))
if (!aDontTouchContent && mEditor->IsInlineNode(node) && mEditor->IsContainer(node))
{
nsCOMPtr<nsISupportsArray> arrayOfInlines;
res = BustUpInlinesAtBRs(node, &arrayOfInlines);
@ -2735,7 +2945,8 @@ nsHTMLEditRules::GetChildNodesForOperation(nsIDOMNode *inNode,
// GetListActionNodes:
//
nsresult
nsHTMLEditRules::GetListActionNodes(nsCOMPtr<nsISupportsArray> *outArrayOfNodes)
nsHTMLEditRules::GetListActionNodes(nsCOMPtr<nsISupportsArray> *outArrayOfNodes,
PRBool aDontTouchContent)
{
if (!outArrayOfNodes) return NS_ERROR_NULL_POINTER;
@ -2748,7 +2959,58 @@ nsHTMLEditRules::GetListActionNodes(nsCOMPtr<nsISupportsArray> *outArrayOfNodes)
if (NS_FAILED(res)) return res;
// use these ranges to contruct a list of nodes to act on.
res = GetNodesForOperation(arrayOfRanges, outArrayOfNodes, kMakeList);
res = GetNodesForOperation(arrayOfRanges, outArrayOfNodes, kMakeList, aDontTouchContent);
if (NS_FAILED(res)) return res;
// pre process our list of nodes...
PRUint32 listCount;
PRInt32 i;
(*outArrayOfNodes)->Count(&listCount);
for (i=(PRInt32)listCount-1; i>=0; i--)
{
nsCOMPtr<nsISupports> isupports = (dont_AddRef)((*outArrayOfNodes)->ElementAt(i));
nsCOMPtr<nsIDOMNode> testNode( do_QueryInterface(isupports ) );
// Remove all non-editable nodes. Leave them be.
if (!mEditor->IsEditable(testNode))
{
(*outArrayOfNodes)->RemoveElementAt(i);
}
// scan for table elements. If we find table elements other than table,
// replace it with a list of any editable non-table content.
if (mEditor->IsTableElement(testNode) && !mEditor->IsTable(testNode))
{
(*outArrayOfNodes)->RemoveElementAt(i);
nsCOMPtr<nsISupportsArray> arrayOfTableContent;
res = GetTableContent(testNode, &arrayOfTableContent);
if (NS_FAILED(res)) return res;
(*outArrayOfNodes)->AppendElements(arrayOfTableContent);
}
}
return res;
}
///////////////////////////////////////////////////////////////////////////
// GetParagraphFormatNodes:
//
nsresult
nsHTMLEditRules::GetParagraphFormatNodes(nsCOMPtr<nsISupportsArray> *outArrayOfNodes,
PRBool aDontTouchContent)
{
if (!outArrayOfNodes) return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsIDOMSelection>selection;
nsresult res = mEditor->GetSelection(getter_AddRefs(selection));
if (NS_FAILED(res)) return res;
nsCOMPtr<nsISupportsArray> arrayOfRanges;
res = GetPromotedRanges(selection, &arrayOfRanges, kMakeList);
if (NS_FAILED(res)) return res;
// use these ranges to contruct a list of nodes to act on.
res = GetNodesForOperation(arrayOfRanges, outArrayOfNodes, kMakeBasicBlock, aDontTouchContent);
if (NS_FAILED(res)) return res;
// pre process our list of nodes...
@ -3285,7 +3547,7 @@ nsHTMLEditRules::ShouldMakeEmptyBlock(nsIDOMSelection *aSelection,
// Note that if _nothing_ should happen, ie, the selection is
// already entireyl inside a block (or blocks) or the correct type,
// then you don't want to return true in outMakeEmpty, since the
// defualt code will insert a new empty block anyway, rather than
// default code will insert a new empty block anyway, rather than
// doing nothing. So we have to detect that case and return false.
if (!aSelection || !outMakeEmpty) return NS_ERROR_NULL_POINTER;
@ -3342,18 +3604,7 @@ nsHTMLEditRules::ShouldMakeEmptyBlock(nsIDOMSelection *aSelection,
*outMakeEmpty = PR_FALSE;
return res;
}
// in an empty block?
PRBool bIsEmptyNode;
// the PR_TRUE param tell IsEmptyNode to not count moz-BRs as content
res = IsEmptyNode(block, &bIsEmptyNode, PR_TRUE, PR_FALSE);
if (bIsEmptyNode)
{
// we must be in a text or inline node - convert existing block
*outMakeEmpty = PR_TRUE;
return res;
}
// is it after a <br> with no inline nodes after it, or a <br> after it??
if (offset)
{
@ -3383,7 +3634,7 @@ nsHTMLEditRules::ShouldMakeEmptyBlock(nsIDOMSelection *aSelection,
// we are after a <br> and not before inline content,
// or we are between <br>s.
// make an empty block
*outMakeEmpty = PR_FALSE;
*outMakeEmpty = PR_TRUE;
return res;
}
}

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

@ -55,6 +55,7 @@ public:
// nsIHTMLEditRules methods
NS_IMETHOD GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL);
NS_IMETHOD GetIndentState(PRBool &aCanIndent, PRBool &aCanOutdent);
NS_IMETHOD GetParagraphState(PRBool &aMixed, nsString &outFormat);
// nsIEditActionListener methods
@ -97,11 +98,12 @@ protected:
nsresult WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel, PRBool *aHandled);
nsresult WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::EDirection aAction,
PRBool *aCancel, PRBool *aHandled);
nsresult WillMakeList(nsIDOMSelection *aSelection, PRBool aOrderd, PRBool *aCancel, PRBool *aHandled);
nsresult WillMakeList(nsIDOMSelection *aSelection, const nsString *aListType, PRBool *aCancel, PRBool *aHandled, const nsString *aItemType=nsnull);
nsresult WillRemoveList(nsIDOMSelection *aSelection, PRBool aOrderd, PRBool *aCancel, PRBool *aHandled);
nsresult WillIndent(nsIDOMSelection *aSelection, PRBool *aCancel, PRBool *aHandled);
nsresult WillOutdent(nsIDOMSelection *aSelection, PRBool *aCancel, PRBool *aHandled);
nsresult WillAlign(nsIDOMSelection *aSelection, const nsString *alignType, PRBool *aCancel, PRBool *aHandled);
nsresult WillMakeDefListItem(nsIDOMSelection *aSelection, const nsString *aBlockType, PRBool *aCancel, PRBool *aHandled);
nsresult WillMakeBasicBlock(nsIDOMSelection *aSelection, const nsString *aBlockType, PRBool *aCancel, PRBool *aHandled);
nsresult DidMakeBasicBlock(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, nsresult aResult);
@ -115,17 +117,13 @@ protected:
nsresult ReturnInParagraph(nsIDOMSelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset, PRBool *aCancel, PRBool *aHandled);
nsresult ReturnInListItem(nsIDOMSelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset);
nsresult AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection);
nsresult ConvertListType(nsIDOMNode *aList, nsCOMPtr<nsIDOMNode> *outList, const nsString& aListType, const nsString& aItemType);
nsresult CreateStyleForInsertText(nsIDOMSelection *aSelection, nsIDOMDocument *aDoc);
nsresult IsEmptyBlock(nsIDOMNode *aNode,
PRBool *outIsEmptyBlock,
PRBool aMozBRDoesntCount = PR_FALSE,
PRBool aListItemsNotEmpty = PR_FALSE);
#if 0
nsresult IsEmptyNode(nsIDOMNode *aNode,
PRBool *outIsEmptyBlock,
PRBool aMozBRDoesntCount = PR_FALSE,
PRBool aListItemsNotEmpty = PR_FALSE);
#endif
PRBool IsFirstNode(nsIDOMNode *aNode);
PRBool IsLastNode(nsIDOMNode *aNode);
PRBool AtStartOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aBlock);
@ -138,11 +136,13 @@ protected:
PRInt32 inOperationType);
nsresult PromoteRange(nsIDOMRange *inRange, PRInt32 inOperationType);
nsresult GetNodesForOperation(nsISupportsArray *inArrayOfRanges,
nsCOMPtr<nsISupportsArray> *outArrayOfNodes,
PRInt32 inOperationType);
nsCOMPtr<nsISupportsArray> *outArrayOfNodes,
PRInt32 inOperationType,
PRBool aDontTouchContent=PR_FALSE);
nsresult GetChildNodesForOperation(nsIDOMNode *inNode,
nsCOMPtr<nsISupportsArray> *outArrayOfNodes);
nsresult GetListActionNodes(nsCOMPtr<nsISupportsArray> *outArrayOfNodes);
nsCOMPtr<nsISupportsArray> *outArrayOfNodes);
nsresult GetListActionNodes(nsCOMPtr<nsISupportsArray> *outArrayOfNodes, PRBool aDontTouchContent=PR_FALSE);
nsresult GetParagraphFormatNodes(nsCOMPtr<nsISupportsArray> *outArrayOfNodes, PRBool aDontTouchContent=PR_FALSE);
nsresult BustUpInlinesAtBRs(nsIDOMNode *inNode,
nsCOMPtr<nsISupportsArray> *outArrayOfNodes);
nsresult MakeTransitionList(nsISupportsArray *inArrayOfNodes,
@ -185,7 +185,6 @@ protected:
nsCOMPtr<nsIDOMRange> mDocChangeRange;
PRBool mListenerEnabled;
nsCOMPtr<nsIDOMRange> mUtilRange;
nsCOMPtr<nsIDOMNode> mBody;
PRUint32 mJoinOffset; // need to remember an int across willJoin/didJoin...
};

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

@ -220,7 +220,9 @@ nsHTMLEditUtils::IsListItem(nsIDOMNode *node)
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if (tag.EqualsWithConversion("li"))
if (tag.EqualsWithConversion("li") ||
tag.EqualsWithConversion("dd") ||
tag.EqualsWithConversion("dt"))
{
return PR_TRUE;
}
@ -289,7 +291,8 @@ nsHTMLEditUtils::IsList(nsIDOMNode *node)
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if ( (tag.EqualsWithConversion("ol")) ||
if ( (tag.EqualsWithConversion("dl")) ||
(tag.EqualsWithConversion("ol")) ||
(tag.EqualsWithConversion("ul")) )
{
return PR_TRUE;
@ -299,7 +302,7 @@ nsHTMLEditUtils::IsList(nsIDOMNode *node)
///////////////////////////////////////////////////////////////////////////
// IsOrderedList: true if node an html orderd list
// IsOrderedList: true if node an html ordered list
//
PRBool
nsHTMLEditUtils::IsOrderedList(nsIDOMNode *node)
@ -317,7 +320,7 @@ nsHTMLEditUtils::IsOrderedList(nsIDOMNode *node)
///////////////////////////////////////////////////////////////////////////
// IsUnorderedList: true if node an html orderd list
// IsUnorderedList: true if node an html unordered list
//
PRBool
nsHTMLEditUtils::IsUnorderedList(nsIDOMNode *node)
@ -334,6 +337,24 @@ nsHTMLEditUtils::IsUnorderedList(nsIDOMNode *node)
}
///////////////////////////////////////////////////////////////////////////
// IsDefinitionList: true if node an html definition list
//
PRBool
nsHTMLEditUtils::IsDefinitionList(nsIDOMNode *node)
{
NS_PRECONDITION(node, "null parent passed to nsHTMLEditUtils::IsDefinitionList");
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if (tag.EqualsWithConversion("dl"))
{
return PR_TRUE;
}
return PR_FALSE;
}
///////////////////////////////////////////////////////////////////////////
// IsBlockquote: true if node an html blockquote node
//

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

@ -48,8 +48,9 @@ public:
static PRBool IsTableRow(nsIDOMNode *aNode);
static PRBool IsTableCell(nsIDOMNode *aNode);
static PRBool IsList(nsIDOMNode *aNode);
static PRBool IsUnorderedList(nsIDOMNode *aNode);
static PRBool IsOrderedList(nsIDOMNode *aNode);
static PRBool IsUnorderedList(nsIDOMNode *aNode);
static PRBool IsDefinitionList(nsIDOMNode *aNode);
static PRBool IsBlockquote(nsIDOMNode *aNode);
static PRBool IsPre(nsIDOMNode *aNode);
static PRBool IsAddress(nsIDOMNode *aNode);

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

@ -1001,6 +1001,10 @@ NS_IMETHODIMP nsHTMLEditor::InsertBR(nsCOMPtr<nsIDOMNode> *outBRNode)
if (!outBRNode) return NS_ERROR_NULL_POINTER;
*outBRNode = nsnull;
// calling it text insertion to trigger moz br treatment by rules
nsAutoRules beginRulesSniffing(this, kOpInsertText, nsIEditor::eNext);
nsresult res = GetSelection(getter_AddRefs(selection));
if (NS_FAILED(res)) return res;
res = selection->GetIsCollapsed(&bCollapsed);
@ -1019,6 +1023,8 @@ NS_IMETHODIMP nsHTMLEditor::InsertBR(nsCOMPtr<nsIDOMNode> *outBRNode)
if (NS_FAILED(res)) return res;
// position selection after br
res = GetNodeLocation(*outBRNode, &selNode, &selOffset);
if (NS_FAILED(res)) return res;
selection->SetHint(PR_TRUE);
res = selection->Collapse(selNode, selOffset+1);
@ -1127,22 +1133,28 @@ NS_IMETHODIMP nsHTMLEditor::SetInlineProperty(nsIAtom *aProperty,
if (NS_FAILED(res)) return res;
// iterate range and build up array
iter->Init(range);
while (NS_ENUMERATOR_FALSE == iter->IsDone())
res = iter->Init(range);
// init returns an error if no nodes in range.
// this can easily happen with the subtree
// iterator if the selection doesn't contain
// any *whole* nodes.
if (NS_SUCCEEDED(res))
{
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);
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;
if (IsEditable(node))
{
isupports = do_QueryInterface(node);
arrayOfNodes->AppendElement(isupports);
}
res = iter->Next();
if (NS_FAILED(res)) return res;
}
res = iter->Next();
if (NS_FAILED(res)) return res;
}
// MOOSE: workaround for selection bug:
//selection->ClearSelection();
@ -1713,7 +1725,20 @@ PRBool nsHTMLEditor::IsAtEndOfNode(nsIDOMNode *aNode, PRInt32 aOffset)
NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty,
const nsString *aAttribute,
const nsString *aValue,
PRBool &aFirst, PRBool &aAny, PRBool &aAll)
PRBool &aFirst,
PRBool &aAny,
PRBool &aAll)
{
return GetInlinePropertyWithAttrValue( aProperty, aAttribute, aValue, aFirst, aAny, aAll, nsnull);
}
NS_IMETHODIMP nsHTMLEditor::GetInlinePropertyWithAttrValue(nsIAtom *aProperty,
const nsString *aAttribute,
const nsString *aValue,
PRBool &aFirst,
PRBool &aAny,
PRBool &aAll,
nsString *outValue)
{
if (!aProperty)
return NS_ERROR_NULL_POINTER;
@ -1805,11 +1830,6 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty,
}
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
@ -1822,6 +1842,7 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty,
iter->Init(range);
nsCOMPtr<nsIContent> content;
nsAutoString firstValue, theValue;
iter->CurrentNode(getter_AddRefs(content));
while (NS_ENUMERATOR_FALSE == iter->IsDone())
{
@ -1866,12 +1887,20 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty,
{
PRBool isSet;
nsCOMPtr<nsIDOMNode>resultNode;
IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet, getter_AddRefs(resultNode));
if (first)
{
IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet, getter_AddRefs(resultNode), &firstValue);
aFirst = isSet;
first = PR_FALSE;
if (outValue) *outValue = firstValue;
}
else
{
IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet, getter_AddRefs(resultNode), &theValue);
if (firstValue != theValue)
aAll = PR_FALSE;
}
if (isSet) {
aAny = PR_TRUE;
}
@ -2790,7 +2819,10 @@ nsHTMLEditor::SetParagraphFormat(const nsString& aParagraphFormat)
{
nsAutoString tag; tag.Assign(aParagraphFormat);
tag.ToLowerCase();
return InsertBasicBlock(tag);
if (tag.EqualsWithConversion("dd") || tag.EqualsWithConversion("dt"))
return MakeDefinitionItem(tag);
else
return InsertBasicBlock(tag);
}
// XXX: ERROR_HANDLING -- this method needs a little work to ensure all error codes are
@ -2907,20 +2939,52 @@ nsHTMLEditor::GetParentBlockTags(nsStringArray *aTagList, PRBool aGetLists)
}
// get the paragraph style(s) for the selection
NS_IMETHODIMP
nsHTMLEditor::GetParagraphTags(nsStringArray *aTagList)
nsHTMLEditor::GetParagraphState(PRBool &aMixed, nsString &outFormat)
{
#if 0
if (gNoisy) { printf("---------- nsHTMLEditor::GetPargraphTags ----------\n"); }
#endif
return GetParentBlockTags(aTagList, PR_FALSE);
if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
nsCOMPtr<nsIHTMLEditRules> htmlRules = do_QueryInterface(mRules);
if (!htmlRules) return NS_ERROR_FAILURE;
return htmlRules->GetParagraphState(aMixed, outFormat);
}
NS_IMETHODIMP
nsHTMLEditor::GetListTags(nsStringArray *aTagList)
nsHTMLEditor::GetFontFaceState(PRBool &aMixed, nsString &outFace)
{
return GetParentBlockTags(aTagList, PR_TRUE);
aMixed = PR_TRUE;
outFace.AssignWithConversion("");
nsresult res;
nsAutoString faceStr; faceStr.AssignWithConversion("face");
PRBool first, any, all;
res = GetInlinePropertyWithAttrValue(nsIEditProperty::font, &faceStr, nsnull, first, any, all, &outFace);
if (NS_FAILED(res)) return res;
if (any && !all) return res; // mixed
if (all)
{
aMixed = PR_FALSE;
return res;
}
res = GetInlineProperty(nsIEditProperty::tt, nsnull, nsnull, first, any, all);
if (NS_FAILED(res)) return res;
if (any && !all) return res; // mixed
if (all)
{
aMixed = PR_FALSE;
nsIEditProperty::tt->ToString(outFace);
}
if (!any)
{
// there was no font face attrs of any kind. We are in normal font.
outFace.AssignWithConversion("");
aMixed = PR_FALSE;
}
return res;
}
NS_IMETHODIMP
@ -2934,6 +2998,17 @@ nsHTMLEditor::GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL)
return htmlRules->GetListState(aMixed, aOL, aUL);
}
NS_IMETHODIMP
nsHTMLEditor::GetIndentState(PRBool &aCanIndent, PRBool &aCanOutdent)
{
if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
nsCOMPtr<nsIHTMLEditRules> htmlRules = do_QueryInterface(mRules);
if (!htmlRules) return NS_ERROR_FAILURE;
return htmlRules->GetIndentState(aCanIndent, aCanOutdent);
}
NS_IMETHODIMP
nsHTMLEditor::MakeOrChangeList(const nsString& aListType)
{
@ -2952,8 +3027,7 @@ nsHTMLEditor::MakeOrChangeList(const nsString& aListType)
if (!selection) return NS_ERROR_NULL_POINTER;
nsTextRulesInfo ruleInfo(nsTextEditRules::kMakeList);
if (aListType.EqualsWithConversion("ol")) ruleInfo.bOrdered = PR_TRUE;
else ruleInfo.bOrdered = PR_FALSE;
ruleInfo.blockType = &aListType;
res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
if (cancel || (NS_FAILED(res))) return res;
@ -3041,8 +3115,37 @@ nsHTMLEditor::RemoveList(const nsString& aListType)
return res;
}
nsresult
nsHTMLEditor::MakeDefinitionItem(const nsString& aItemType)
{
nsresult res;
if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
NS_IMETHODIMP
nsCOMPtr<nsIDOMSelection> selection;
PRBool cancel, handled;
nsAutoEditBatch beginBatching(this);
nsAutoRules beginRulesSniffing(this, kOpMakeDefListItem, nsIEditor::eNext);
// pre-process
res = GetSelection(getter_AddRefs(selection));
if (NS_FAILED(res)) return res;
if (!selection) return NS_ERROR_NULL_POINTER;
nsTextRulesInfo ruleInfo(nsTextEditRules::kMakeDefListItem);
ruleInfo.blockType = &aItemType;
res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
if (cancel || (NS_FAILED(res))) return res;
if (!handled)
{
// todo: no default for now. we count on rules to handle it.
}
res = mRules->DidDoAction(selection, &ruleInfo, res);
return res;
}
nsresult
nsHTMLEditor::InsertBasicBlock(const nsString& aBlockType)
{
nsresult res;
@ -5534,7 +5637,8 @@ void nsHTMLEditor::IsTextPropertySetByContent(nsIDOMNode *aNode,
const nsString *aAttribute,
const nsString *aValue,
PRBool &aIsSet,
nsIDOMNode **aStyleNode) const
nsIDOMNode **aStyleNode,
nsString *outValue) const
{
nsresult result;
aIsSet = PR_FALSE; // must be initialized to false for code below to work
@ -5548,15 +5652,15 @@ void nsHTMLEditor::IsTextPropertySetByContent(nsIDOMNode *aNode,
element = do_QueryInterface(node);
if (element)
{
nsAutoString tag;
nsAutoString tag, value;
element->GetTagName(tag);
if (propName.EqualsIgnoreCase(tag))
{
PRBool found = PR_FALSE;
if (aAttribute && 0!=aAttribute->Length())
{
nsAutoString value;
element->GetAttribute(*aAttribute, value);
if (outValue) *outValue = value;
if (value.Length())
{
if (!aValue) {
@ -6182,12 +6286,14 @@ nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange)
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 it's collapsed set typing state
if (bCollapsed)
{
return NS_OK;
nsCOMPtr<nsIAtom> atom;
if (aSizeChange==1) atom = nsIEditProperty::big;
else atom = nsIEditProperty::small;
// manipulating text attributes on a collapsed selection only sets state for the next text insertion
return mTypeInState->SetProp(atom, nsnull, nsnull);
}
// wrap with txn batching, rules sniffing, and selection preservation code
@ -6346,6 +6452,9 @@ nsHTMLEditor::RelativeFontChangeOnTextNode( PRInt32 aSizeChange,
PRUint32 textLen;
aTextNode->GetLength(&textLen);
// -1 is a magic value meaning to the end of node
if (aEndOffset == -1) aEndOffset = textLen;
if ( (PRUint32)aEndOffset != textLen )
{
// we need to split off back of text node

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

@ -69,6 +69,7 @@ public:
kOpAlign = 3004,
kOpMakeBasicBlock = 3005,
kOpRemoveList = 3006,
kOpMakeDefListItem = 3007,
kOpInsertElement = 3008,
kOpInsertQuotation = 3009
};
@ -103,6 +104,11 @@ public:
const nsString *aAttribute,
const nsString *aValue,
PRBool &aFirst, PRBool &aAny, PRBool &aAll);
NS_IMETHOD GetInlinePropertyWithAttrValue(nsIAtom *aProperty,
const nsString *aAttribute,
const nsString *aValue,
PRBool &aFirst, PRBool &aAny, PRBool &aAll,
nsString *outValue);
NS_IMETHOD RemoveAllInlineProperties();
NS_IMETHOD RemoveInlineProperty(nsIAtom *aProperty, const nsString *aAttribute);
@ -125,13 +131,14 @@ public:
NS_IMETHOD SetParagraphFormat(const nsString& aParagraphFormat);
NS_IMETHOD GetParentBlockTags(nsStringArray *aTagList, PRBool aGetLists);
NS_IMETHOD GetParagraphTags(nsStringArray *aTagList);
NS_IMETHOD GetListTags(nsStringArray *aTagList);
NS_IMETHOD GetParagraphState(PRBool &aMixed, nsString &outFormat);
NS_IMETHOD GetFontFaceState(PRBool &aMixed, nsString &outFace);
NS_IMETHOD GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL);
NS_IMETHOD GetIndentState(PRBool &aCanIndent, PRBool &aCanOutdent);
NS_IMETHOD MakeOrChangeList(const nsString& aListType);
NS_IMETHOD RemoveList(const nsString& aListType);
NS_IMETHOD InsertBasicBlock(const nsString& aBlockType);
NS_IMETHOD Indent(const nsString& aIndent);
NS_IMETHOD Align(const nsString& aAlign);
@ -441,7 +448,8 @@ protected:
const nsString *aAttribute,
const nsString *aValue,
PRBool &aIsSet,
nsIDOMNode **aStyleNode) const;
nsIDOMNode **aStyleNode,
nsString *outValue = nsnull) const;
/** style-based query returns PR_TRUE if (aProperty, aAttribute) is set in aSC.
* WARNING: not well tested yet since we don't do style-based queries anywhere.
@ -473,6 +481,10 @@ protected:
/* small utility routine to test the eEditorReadonly bit */
PRBool IsModifiable();
/* helpers for block transformations */
nsresult MakeDefinitionItem(const nsString& aItemType);
nsresult InsertBasicBlock(const nsString& aBlockType);
/* increase/decrease the font size of selection */
nsresult RelativeFontChange( PRInt32 aSizeChange);

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

@ -70,6 +70,9 @@ NS_NewTextEditRules(nsIEditRules** aInstancePtrResult)
nsTextEditRules::nsTextEditRules()
: mEditor(nsnull)
, mFlags(0) // initialized to 0 ("no flags set"). Real initial value is given in Init()
, mPasswordText()
, mBogusNode(nsnull)
, mBody(nsnull)
, mActionNesting(0)
, mLockRulesSniffing(PR_FALSE)
, mTheAction(0)
@ -106,29 +109,33 @@ nsTextEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags)
nsCOMPtr<nsIDOMSelection> selection;
mEditor->GetSelection(getter_AddRefs(selection));
NS_ASSERTION(selection, "editor cannot get selection");
nsresult res = CreateBogusNodeIfNeeded(selection); // this method handles null selection, which should never happen anyway
// create a range that is the entire body contents
if (NS_FAILED(res)) return res;
// remember our root node
nsCOMPtr<nsIDOMElement> bodyElement;
res = mEditor->GetRootElement(getter_AddRefs(bodyElement));
nsresult res = mEditor->GetRootElement(getter_AddRefs(bodyElement));
if (NS_FAILED(res)) return res;
if (!bodyElement) return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsIDOMNode>bodyNode = do_QueryInterface(bodyElement);
if (!bodyNode) return NS_ERROR_FAILURE;
mBody = do_QueryInterface(bodyElement);
if (!mBody) return NS_ERROR_FAILURE;
// put in a magic br if needed
res = CreateBogusNodeIfNeeded(selection); // this method handles null selection, which should never happen anyway
if (NS_FAILED(res)) return res;
// create a range that is the entire body contents
nsCOMPtr<nsIDOMRange> wholeDoc;
res = nsComponentManager::CreateInstance(kRangeCID, nsnull, NS_GET_IID(nsIDOMRange),
getter_AddRefs(wholeDoc));
if (NS_FAILED(res)) return res;
wholeDoc->SetStart(bodyNode,0);
wholeDoc->SetStart(mBody,0);
nsCOMPtr<nsIDOMNodeList> list;
res = bodyNode->GetChildNodes(getter_AddRefs(list));
res = mBody->GetChildNodes(getter_AddRefs(list));
if (NS_FAILED(res) || !list) return res?res:NS_ERROR_FAILURE;
PRUint32 listCount;
res = list->GetLength(&listCount);
if (NS_FAILED(res)) return res;
res = wholeDoc->SetEnd(bodyNode,listCount);
res = wholeDoc->SetEnd(mBody,listCount);
if (NS_FAILED(res)) return res;
// replace newlines in that range with breaks
@ -1188,19 +1195,14 @@ nsTextEditRules::CreateBogusNodeIfNeeded(nsIDOMSelection *aSelection)
// tell rules system to not do any post-processing
nsAutoRules beginRulesSniffing(mEditor, nsEditor::kOpIgnore, nsIEditor::eNone);
nsCOMPtr<nsIDOMElement> bodyElement;
nsresult res = mEditor->GetRootElement(getter_AddRefs(bodyElement));
if (NS_FAILED(res)) return res;
if (!bodyElement) return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsIDOMNode>bodyNode = do_QueryInterface(bodyElement);
if (!mBody) return NS_ERROR_NULL_POINTER;
// now we've got the body tag.
// iterate the body tag, looking for editable content
// if no editable content is found, insert the bogus node
PRBool needsBogusContent=PR_TRUE;
nsCOMPtr<nsIDOMNode>bodyChild;
res = bodyNode->GetFirstChild(getter_AddRefs(bodyChild));
nsresult res = mBody->GetFirstChild(getter_AddRefs(bodyChild));
while ((NS_SUCCEEDED(res)) && bodyChild)
{
if (mEditor->IsMozEditorBogusNode(bodyChild) || mEditor->IsEditable(bodyChild))
@ -1214,25 +1216,27 @@ nsTextEditRules::CreateBogusNodeIfNeeded(nsIDOMSelection *aSelection)
}
if (needsBogusContent)
{
// set mBogusNode to be the newly created <br>
res = mEditor->CreateNode(NS_ConvertASCIItoUCS2("br"), bodyNode, 0,
getter_AddRefs(mBogusNode));
// create a br
nsCOMPtr<nsIDOMDocument> domDoc;
res = mEditor->GetDocument(getter_AddRefs(domDoc));
nsCOMPtr<nsIDOMElement>brElement;
res = domDoc->CreateElement(NS_ConvertASCIItoUCS2("br"),getter_AddRefs(brElement));
if (NS_FAILED(res)) return res;
// set mBogusNode to be the newly created <br>
mBogusNode = do_QueryInterface(brElement);
if (!mBogusNode) return NS_ERROR_NULL_POINTER;
// give it a special attribute
nsCOMPtr<nsIDOMElement>newPElement;
newPElement = do_QueryInterface(mBogusNode);
if (newPElement)
{
newPElement->SetAttribute(
NS_ConvertASCIItoUCS2(nsEditor::kMOZEditorBogusNodeAttr),
NS_ConvertASCIItoUCS2(nsEditor::kMOZEditorBogusNodeValue)
);
}
brElement->SetAttribute( NS_ConvertASCIItoUCS2(nsEditor::kMOZEditorBogusNodeAttr),
NS_ConvertASCIItoUCS2(nsEditor::kMOZEditorBogusNodeValue) );
// put the node in the document
res = mEditor->InsertNode(mBogusNode,mBody,0);
if (NS_FAILED(res)) return res;
// set selection
aSelection->Collapse(bodyNode,0);
aSelection->Collapse(mBody,0);
}
return res;
}

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

@ -81,6 +81,7 @@ public:
kAlign = 3004,
kMakeBasicBlock = 3005,
kRemoveList = 3006,
kMakeDefListItem = 3007,
kInsertElement = 3008
};
@ -173,6 +174,7 @@ protected:
nsHTMLEditor *mEditor; // note that we do not refcount the editor
nsString mPasswordText; // a buffer we use to store the real value of password editors
nsCOMPtr<nsIDOMNode> mBogusNode; // magic node acts as placeholder in empty doc
nsCOMPtr<nsIDOMNode> mBody; // cached root node
PRUint32 mFlags;
PRUint32 mActionNesting;
PRBool mLockRulesSniffing;
@ -195,7 +197,6 @@ class nsTextRulesInfo : public nsRulesInfo
outputFormat(0),
maxLength(-1),
collapsedAction(nsIEditor::eNext),
bOrdered(PR_FALSE),
alignType(0),
blockType(0),
insertElement(0)

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

@ -121,6 +121,12 @@ public:
const nsString *aValue,
PRBool &aFirst, PRBool &aAny, PRBool &aAll)=0;
NS_IMETHOD GetInlinePropertyWithAttrValue(nsIAtom *aProperty,
const nsString *aAttribute,
const nsString *aValue,
PRBool &aFirst, PRBool &aAny, PRBool &aAll,
nsString *outValue)=0;
/**
* RemoveAllInlineProperties() deletes all the inline properties from all
* text in the current selection.
@ -237,16 +243,6 @@ public:
*/
NS_IMETHOD DeleteSelectionAndCreateNode(const nsString& aTag, nsIDOMNode ** aNewNode)=0;
/**
* GetListState returns what list type is in the selection.
* @param aMixed True if there is more than one type of list, or
* if there is some list and non-list
* @param aOL The company that employs me. No, really, it's
* true if an "ol" list is selected.
* @param aUL true if an "ul" list is selected.
*/
NS_IMETHOD GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL)=0;
/* ------------ Selection manipulation -------------- */
/* Should these be moved to nsIDOMSelection? */
@ -281,19 +277,34 @@ public:
NS_IMETHOD SetParagraphFormat(const nsString& aParagraphFormat)=0;
/**
* Get a list of tagnames of all paragraph tags
* (the closest block parent tags) of all
* elements in the current selection
* Document me!
*
*/
NS_IMETHOD GetParagraphTags(nsStringArray *aTagList)=0;
NS_IMETHOD GetParagraphState(PRBool &aMixed, nsString &outFace)=0;
/**
* Get a list of tagnames of all list tags
* (the closest parent tags that are UL, OL, or DL) of all
* elements in the current selection
/**
* GetFontFaceState returns what font face is in the selection.
* @param aMixed True if there is more than one font face
* @param outFace name of face. Note: "tt" is returned for
* tt tag. "" is returned for none.
*/
NS_IMETHOD GetListTags(nsStringArray *aTagList)=0;
NS_IMETHOD GetFontFaceState(PRBool &aMixed, nsString &outFont)=0;
/**
* GetListState returns what list type is in the selection.
* @param aMixed True if there is more than one type of list, or
* if there is some list and non-list
* @param aOL The company that employs me. No, really, it's
* true if an "ol" list is selected.
* @param aUL true if an "ul" list is selected.
*/
NS_IMETHOD GetListState(PRBool &aMixed, PRBool &aOL, PRBool &aUL)=0;
/**
* Document me!
*
*/
NS_IMETHOD GetIndentState(PRBool &aCanIndent, PRBool &aCanOutdent)=0;
/**
* Document me!

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

@ -380,6 +380,7 @@ function EditorSaveDocument(doSaveAs, doSaveCopy)
function EditorCanClose()
{
// Returns FALSE only if user cancels save action
dump("Calling EditorCanClose\n");
return editorShell.CheckAndSaveDocument(GetString("BeforeClosing"));
}
@ -434,65 +435,94 @@ function EditorSetTextProperty(property, attribute, value)
gContentWindow.focus();
}
function onParagraphFormatChange(commandID)
function onParagraphFormatChange(paraMenuList, commandID)
{
var commandNode = document.getElementById(commmandID);
var commandNode = document.getElementById(commandID);
var state = commandNode.getAttribute("state");
// dump(" ==== onParagraphFormatChange was called. state="+state+"|\n");
return; //TODO: REWRITE THIS
dump("Updating font face with " + state + "\n");
// force match with "normal"
if (state == "body")
state = "";
if (state == "mixed")
{
//Selection is the "mixed" ( > 1 style) state
paraMenuList.selectedItem = null;
paraMenuList.setAttribute("value",GetString('MixedFormats'));
}
else
{
var menuPopup = document.getElementById("ParagraphPopup");
var menuItems = menuPopup.childNodes;
for (i=0; i < menuItems.length; i++)
{
var menuItem = menuItems.item(i);
if (menuItem.data == state)
{
paraMenuList.selectedItem = menuItem;
break;
}
}
}
}
function EditorSetParagraphFormat(commandID, paraFormat)
{
// editorShell.SetParagraphFormat(paraFormat);
var commandNode = document.getElementById(commandID);
dump("Saving para format state " + paraFormat + "\n");
commandNode.setAttribute("state", paraFormat);
window.content.focus(); // needed for command dispatch to work
goDoCommand(commandID);
}
function onFontFaceChange()
function onFontFaceChange(fontFaceMenuList, commandID)
{
return; //TODO: REWRITE THIS
var select = document.getElementById("FontFaceSelect");
if (select)
{
// Default selects "Variable Width"
var newIndex = 0;
var face = select.getAttribute("face");
//dump("onFontFaceChange: face="+face+"\n");
if ( face == "mixed")
var commandNode = document.getElementById(commandID);
var state = commandNode.getAttribute("state");
dump("Updating font face with " + state + "\n");
if (state == "mixed")
{
//Selection is the "mixed" ( > 1 style) state
fontFaceMenuList.selectedItem = null;
fontFaceMenuList.setAttribute("value",GetString('MixedFormats'));
}
else
{
var menuPopup = document.getElementById("FontFacePopup");
var menuItems = menuPopup.childNodes;
for (i=0; i < menuItems.length; i++)
{
// No single type selected
newIndex = -1;
}
else
{
for( var i = 0; i < gFontFaceNames.length; i++)
var menuItem = menuItems.item(i);
if (menuItem.getAttribute("value") && (menuItem.data.toLowerCase() == state.toLowerCase()))
{
if( gFontFaceNames[i] == face )
{
newIndex = i;
break;
}
fontFaceMenuList.selectedItem = menuItem;
break;
}
}
if (select.selectedIndex != newIndex)
select.selectedIndex = newIndex;
}
}
function EditorSetFontFace(fontFace)
function EditorSetFontFace(commandID, fontFace)
{
if (fontFace == "tt") {
dump("Setting font face to " + fontFace + "\n");
var commandNode = document.getElementById(commandID);
commandNode.setAttribute("state", fontFace);
window.content.focus(); // needed for command dispatch to work
goDoCommand(commandID);
/*
if (fontFace == "tt")
{
// The old "teletype" attribute
editorShell.SetTextProperty("tt", "", "");
// Clear existing font face
editorShell.RemoveTextProperty("font", "face");
} else {
}
else
{
// Remove any existing TT nodes
editorShell.RemoveTextProperty("tt", "", "");
@ -502,8 +532,8 @@ function EditorSetFontFace(fontFace)
editorShell.SetTextProperty("font", "face", fontFace);
}
}
//dump("Setting focus to content window...\n");
gContentWindow.focus();
window.content.focus();
*/
}
function EditorSelectFontSize()
@ -519,33 +549,29 @@ function EditorSelectFontSize()
}
}
function onFontSizeChange()
function onFontSizeChange(fontSizeMenulist, commandID)
{
var select = document.getElementById("FontFaceSelect");
if (select)
{
// If we don't match anything, set to "0 (normal)"
var newIndex = 2;
var size = select.getAttribute("size");
if ( size == "mixed")
// If we don't match anything, set to "0 (normal)"
var newIndex = 2;
var size = fontSizeMenulist.getAttribute("size");
if ( size == "mixed")
{
// No single type selected
newIndex = -1;
}
else
{
for( var i = 0; i < gFontSizeNames.length; i++)
{
// No single type selected
newIndex = -1;
}
else
{
for( var i = 0; i < gFontSizeNames.length; i++)
if( gFontSizeNames[i] == size )
{
if( gFontSizeNames[i] == size )
{
newIndex = i;
break;
}
newIndex = i;
break;
}
}
if (select.selectedIndex != newIndex)
select.selectedIndex = newIndex;
}
if (fontSizeMenulist.selectedIndex != newIndex)
fontSizeMenulist.selectedIndex = newIndex;
}
function EditorSetFontSize(size)
@ -553,7 +579,7 @@ function EditorSetFontSize(size)
if( size == "0" || size == "normal" ||
size == "medium" )
{
editorShell.RemoveTextProperty("font", size);
editorShell.RemoveTextProperty("font", "size");
dump("Removing font size\n");
} else {
dump("Setting font size\n");
@ -641,7 +667,7 @@ dump("EditorSelectBackColor: "+color+"\n");
if (menupopup) menupopup.closePopup();
EditorSetBackgroundColor(color);
gContentWindow.focus();
window.content.focus();
}
function EditorRemoveBackColor(ColorWellID)
@ -653,7 +679,7 @@ function EditorRemoveBackColor(ColorWellID)
}
//TODO: Set colorwell to browser's default color
editorShell.SetBackgroundColor("");
gContentWindow.focus();
window.content.focus();
}
@ -668,26 +694,25 @@ function SetManualTextColor()
function EditorSetFontColor(color)
{
editorShell.SetTextProperty("font", "color", color);
gContentWindow.focus();
window.content.focus();
}
function EditorSetBackgroundColor(color)
{
editorShell.SetBackgroundColor(color);
gContentWindow.focus();
window.content.focus();
}
function EditorApplyStyle(tagName)
{
dump("applying style\n");
editorShell.SetTextProperty(tagName, "", "");
gContentWindow.focus();
window.content.focus();
}
function EditorRemoveLinks()
{
editorShell.RemoveTextProperty("href", "");
gContentWindow.focus();
window.content.focus();
}
/*TODO: We need an oncreate hook to do enabling/disabling for the
@ -771,8 +796,9 @@ function SetEditMode(mode)
if (bodyNode)
editorShell.editorSelection.collapse(bodyNode, 0);
gContentWindow.focus();
setTimeout("gContentWindow.focus()", 10);
window.content.focus();
// yuck. what is this?
setTimeout("window.content.focus()", 10);
}
}
}
@ -828,7 +854,7 @@ function SetDisplayMode(mode)
// TODO: WE MUST ENABLE APPROPRIATE COMMANDS
// and change UI back to "normal"
gContentWindow.focus();
window.content.focus();
}
return true;
}
@ -919,6 +945,9 @@ function EditorInitEditMenu()
{
var DelStr = GetString(gIsMac ? "Clear" : "Delete");
// Yuck. We should be doing this at build time, using
// platform-specific overlays or dtd files.
// Change menu text to "Clear" for Mac
// TODO: Should this be in globalOverlay.j?
document.getElementById("menu_delete").setAttribute("value",DelStr);

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

@ -182,6 +182,8 @@
<!-- the state attribute gets filled with the paragraph format before the command is exectued -->
<command id="cmd_paragraphState" state="" oncommand="goDoCommand('cmd_paragraphState')"/>
<command id="cmd_fontFace" state="" oncommand="goDoCommand('cmd_fontFace')"/>
<command id="cmd_fontSize" state="" oncommand="goDoCommand('cmd_fontSize')"/>
<command id="cmd_align" state="" oncommand="goDoCommand('cmd_align')"/>
<command id="cmd_advancedProperties" oncommand="goDoCommand('cmd_advancedProperties')"/>
@ -234,7 +236,6 @@
<command id="cmd_viewtaskbar" oncommand="goToggleToolbar('taskbar','cmd_viewtaskbar');" checked="true"/>
<!-- Obsolete; these will go away -->
<command id="Editor:Font:Face" face=""/>
<command id="Editor:Font:Size" fontsize=""/>
</broadcasterset>
@ -430,7 +431,7 @@
<!-- Font face submenu -->
<menu id="fontFaceMenu" value="&fontfaceMenu.label;" accesskey="&formatfontmenu.accesskey;"
position="1">
<menupopup oncommand="EditorSetFontFace(event.target.data)">
<menupopup oncommand="EditorSetFontFace('cmd_fontFace', event.target.data)">
<menuitem value="&fontVarWidth.label;" accesskey="&fontvarwidth.accesskey;" data=""/>
<menuitem value="&fontFixedWidth.label;" accesskey="&fontfixedwidth.accesskey;" data="tt"/>
<menuseparator/>
@ -558,7 +559,7 @@
<menu id="headingMenu" value="&headingMenu.label;"
accesskey="&formatheadingmenu.accesskey;"
position="10">
<menupopup oncommand="EditorSetParagraphFormat('cmd_paragraphState', event.target.data)">
<menupopup oncommand="EditorSetParagraphFormat('cmd_paragraphState', event.target.getAttribute('data'))">
<menuitem value="&normalCmd.label;" accesskey="&normal.accesskey;" data=""/>
<menuitem value="&heading1Cmd.label;" accesskey="&heading1.accesskey;" data="H1"/>
<menuitem value="&heading2Cmd.label;" accesskey="&heading2.accesskey;" data="H2"/>
@ -573,7 +574,7 @@
<menu id="paragraphMenu" value="&paragraphMenu.label;"
accesskey="&formatparagraphmenu.accesskey;"
position="11">
<menupopup oncommand="EditorSetParagraphFormat('cmd_paragraphState', event.target.data)">
<menupopup oncommand="EditorSetParagraphFormat('cmd_paragraphState', event.target.getAttribute('data'))">
<menuitem value="&normalCmd.label;" accesskey="&normal.accesskey;" data=""/>
<menuitem value="&paragraphParagraphCmd.label;" accesskey="&paragraphparagraph.accesskey;" data="P"/>
<menuitem value="&paragraphBlockquoteCmd.label;" accesskey="&paragraphblockquote.accesskey;" data="BLOCKQUOTE"/>
@ -771,8 +772,8 @@
<!-- Formatting toolbar items. "data" are HTML tagnames, don't translate -->
<menulist id="ParagraphSelect" tooltip="aTooltip" tooltiptext="&ParagraphSelect.tooltip;">
<observes element="cmd_paragraphState" attribute="state" onbroadcast="onParagraphFormatChange('cmd_paragraphState')"/>
<menupopup oncommand="EditorSetParagraphFormat('cmd_paragraphState', event.target.data)">
<observes element="cmd_paragraphState" attribute="state" onbroadcast="onParagraphFormatChange(this.parentNode, 'cmd_paragraphState')"/>
<menupopup id="ParagraphPopup" oncommand="EditorSetParagraphFormat('cmd_paragraphState', event.target.data)">
<menuitem value="&normalCmd.label;" data=""/>
<menuitem value="&paragraphParagraphCmd.label;" data="P"/>
<menuitem value="&heading1Cmd.label;" data="H1"/>
@ -792,8 +793,8 @@
<!-- TODO: Use actual "face" value when combobox can display arbitrary HTML -->
<!-- "data" are HTML tagnames, don't translate -->
<menulist id="FontFaceSelect" tooltip="aTooltip" tooltiptext="&FontFaceSelect.tooltip;">
<observes element="Editor:Font:Face" attribute="face" onbroadcast="onFontFaceChange()"/>
<menupopup oncommand="EditorSetFontFace(event.target.data)">
<observes element="cmd_fontFace" attribute="state" onbroadcast="onFontFaceChange(this.parentNode, 'cmd_fontFace')"/>
<menupopup id="FontFacePopup" oncommand="EditorSetFontFace('cmd_fontFace', event.target.data)">
<menuitem value="&fontVarWidth.label;" data=""/>
<menuitem value="&fontFixedWidth.label;" data="tt"/>
<menuseparator/>
@ -804,7 +805,7 @@
</menulist>
<menulist id="FontSizeSelect" oncommand="EditorSelectFontSize()" tooltip="aTooltip" tooltiptext="&FontSizeSelect.tooltip;">
<observes element="Editor:Font:Size" attribute="fontsize" onbroadcast="onFontSizeChange()"/>
<observes element="cmd_fontSize" attribute="state" onbroadcast="onFontSizeChange(this.parentNode, 'cmd_fontSize')"/>
<menupopup>
<menuitem value="&size-xx-smallCmd.label;"/>
<menuitem value="&size-x-smallCmd.label;"/>

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

@ -60,6 +60,8 @@ EmptyHREFError=You must enter or choose a location (URL) to create a new link.
LinkText=Link text:
LinkImage=Link image:
MixedSelection=[Mixed selection]
MixedFormats=(mixed)
MixedFonts=(mixed)
EnterLinkText=Enter text to display for the link:
EmptyLinkTextError=You must enter some text for this link.
#Don't translate: %n% %min% %max%
@ -115,3 +117,4 @@ Clear=Clear
#Mouse actions
Click=Click
Drag=Drag
Unknown=Unknown