editor/libeditor/base/DeleteRangeTxn.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/editor/libeditor/base/DeleteRangeTxn.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,231 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "DeleteNodeTxn.h"
    1.10 +#include "DeleteRangeTxn.h"
    1.11 +#include "DeleteTextTxn.h"
    1.12 +#include "mozilla/Assertions.h"
    1.13 +#include "mozilla/dom/Selection.h"
    1.14 +#include "mozilla/mozalloc.h"
    1.15 +#include "nsCOMPtr.h"
    1.16 +#include "nsDebug.h"
    1.17 +#include "nsEditor.h"
    1.18 +#include "nsError.h"
    1.19 +#include "nsIContent.h"
    1.20 +#include "nsIContentIterator.h"
    1.21 +#include "nsIDOMCharacterData.h"
    1.22 +#include "nsINode.h"
    1.23 +#include "nsAString.h"
    1.24 +
    1.25 +class nsIDOMRange;
    1.26 +
    1.27 +using namespace mozilla;
    1.28 +using namespace mozilla::dom;
    1.29 +
    1.30 +// note that aEditor is not refcounted
    1.31 +DeleteRangeTxn::DeleteRangeTxn()
    1.32 +  : EditAggregateTxn(),
    1.33 +    mRange(),
    1.34 +    mEditor(nullptr),
    1.35 +    mRangeUpdater(nullptr)
    1.36 +{
    1.37 +}
    1.38 +
    1.39 +NS_IMPL_CYCLE_COLLECTION_INHERITED(DeleteRangeTxn, EditAggregateTxn,
    1.40 +                                   mRange)
    1.41 +
    1.42 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeleteRangeTxn)
    1.43 +NS_INTERFACE_MAP_END_INHERITING(EditAggregateTxn)
    1.44 +
    1.45 +nsresult
    1.46 +DeleteRangeTxn::Init(nsEditor* aEditor,
    1.47 +                     nsRange* aRange,
    1.48 +                     nsRangeUpdater* aRangeUpdater)
    1.49 +{
    1.50 +  MOZ_ASSERT(aEditor && aRange);
    1.51 +
    1.52 +  mEditor = aEditor;
    1.53 +  mRange = aRange->CloneRange();
    1.54 +  mRangeUpdater = aRangeUpdater;
    1.55 +
    1.56 +  NS_ENSURE_TRUE(mEditor->IsModifiableNode(mRange->GetStartParent()),
    1.57 +                 NS_ERROR_FAILURE);
    1.58 +  NS_ENSURE_TRUE(mEditor->IsModifiableNode(mRange->GetEndParent()),
    1.59 +                 NS_ERROR_FAILURE);
    1.60 +  NS_ENSURE_TRUE(mEditor->IsModifiableNode(mRange->GetCommonAncestor()),
    1.61 +                 NS_ERROR_FAILURE);
    1.62 +
    1.63 +  return NS_OK;
    1.64 +}
    1.65 +
    1.66 +NS_IMETHODIMP
    1.67 +DeleteRangeTxn::DoTransaction()
    1.68 +{
    1.69 +  MOZ_ASSERT(mRange && mEditor);
    1.70 +  nsresult res;
    1.71 +
    1.72 +  // build the child transactions
    1.73 +  nsCOMPtr<nsINode> startParent = mRange->GetStartParent();
    1.74 +  int32_t startOffset = mRange->StartOffset();
    1.75 +  nsCOMPtr<nsINode> endParent = mRange->GetEndParent();
    1.76 +  int32_t endOffset = mRange->EndOffset();
    1.77 +  MOZ_ASSERT(startParent && endParent);
    1.78 +
    1.79 +  if (startParent == endParent) {
    1.80 +    // the selection begins and ends in the same node
    1.81 +    res = CreateTxnsToDeleteBetween(startParent, startOffset, endOffset);
    1.82 +    NS_ENSURE_SUCCESS(res, res);
    1.83 +  } else {
    1.84 +    // the selection ends in a different node from where it started.  delete
    1.85 +    // the relevant content in the start node
    1.86 +    res = CreateTxnsToDeleteContent(startParent, startOffset, nsIEditor::eNext);
    1.87 +    NS_ENSURE_SUCCESS(res, res);
    1.88 +    // delete the intervening nodes
    1.89 +    res = CreateTxnsToDeleteNodesBetween();
    1.90 +    NS_ENSURE_SUCCESS(res, res);
    1.91 +    // delete the relevant content in the end node
    1.92 +    res = CreateTxnsToDeleteContent(endParent, endOffset, nsIEditor::ePrevious);
    1.93 +    NS_ENSURE_SUCCESS(res, res);
    1.94 +  }
    1.95 +
    1.96 +  // if we've successfully built this aggregate transaction, then do it.
    1.97 +  res = EditAggregateTxn::DoTransaction();
    1.98 +  NS_ENSURE_SUCCESS(res, res);
    1.99 +
   1.100 +  // only set selection to deletion point if editor gives permission
   1.101 +  bool bAdjustSelection;
   1.102 +  mEditor->ShouldTxnSetSelection(&bAdjustSelection);
   1.103 +  if (bAdjustSelection) {
   1.104 +    nsRefPtr<Selection> selection = mEditor->GetSelection();
   1.105 +    NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
   1.106 +    res = selection->Collapse(startParent, startOffset);
   1.107 +    NS_ENSURE_SUCCESS(res, res);
   1.108 +  }
   1.109 +  // else do nothing - dom range gravity will adjust selection
   1.110 +
   1.111 +  return NS_OK;
   1.112 +}
   1.113 +
   1.114 +NS_IMETHODIMP
   1.115 +DeleteRangeTxn::UndoTransaction()
   1.116 +{
   1.117 +  MOZ_ASSERT(mRange && mEditor);
   1.118 +
   1.119 +  return EditAggregateTxn::UndoTransaction();
   1.120 +}
   1.121 +
   1.122 +NS_IMETHODIMP
   1.123 +DeleteRangeTxn::RedoTransaction()
   1.124 +{
   1.125 +  MOZ_ASSERT(mRange && mEditor);
   1.126 +
   1.127 +  return EditAggregateTxn::RedoTransaction();
   1.128 +}
   1.129 +
   1.130 +NS_IMETHODIMP
   1.131 +DeleteRangeTxn::GetTxnDescription(nsAString& aString)
   1.132 +{
   1.133 +  aString.AssignLiteral("DeleteRangeTxn");
   1.134 +  return NS_OK;
   1.135 +}
   1.136 +
   1.137 +nsresult
   1.138 +DeleteRangeTxn::CreateTxnsToDeleteBetween(nsINode* aNode,
   1.139 +                                          int32_t aStartOffset,
   1.140 +                                          int32_t aEndOffset)
   1.141 +{
   1.142 +  // see what kind of node we have
   1.143 +  if (aNode->IsNodeOfType(nsINode::eDATA_NODE)) {
   1.144 +    // if the node is a chardata node, then delete chardata content
   1.145 +    nsRefPtr<DeleteTextTxn> txn = new DeleteTextTxn();
   1.146 +
   1.147 +    int32_t numToDel;
   1.148 +    if (aStartOffset == aEndOffset) {
   1.149 +      numToDel = 1;
   1.150 +    } else {
   1.151 +      numToDel = aEndOffset - aStartOffset;
   1.152 +    }
   1.153 +
   1.154 +    nsCOMPtr<nsIDOMCharacterData> charDataNode = do_QueryInterface(aNode);
   1.155 +    nsresult res = txn->Init(mEditor, charDataNode, aStartOffset, numToDel,
   1.156 +                             mRangeUpdater);
   1.157 +    NS_ENSURE_SUCCESS(res, res);
   1.158 +
   1.159 +    AppendChild(txn);
   1.160 +    return NS_OK;
   1.161 +  }
   1.162 +
   1.163 +  nsCOMPtr<nsIContent> child = aNode->GetChildAt(aStartOffset);
   1.164 +  NS_ENSURE_STATE(child);
   1.165 +
   1.166 +  nsresult res = NS_OK;
   1.167 +  for (int32_t i = aStartOffset; i < aEndOffset; ++i) {
   1.168 +    nsRefPtr<DeleteNodeTxn> txn = new DeleteNodeTxn();
   1.169 +    res = txn->Init(mEditor, child, mRangeUpdater);
   1.170 +    if (NS_SUCCEEDED(res)) {
   1.171 +      AppendChild(txn);
   1.172 +    }
   1.173 +
   1.174 +    child = child->GetNextSibling();
   1.175 +  }
   1.176 +
   1.177 +  NS_ENSURE_SUCCESS(res, res);
   1.178 +  return NS_OK;
   1.179 +}
   1.180 +
   1.181 +nsresult
   1.182 +DeleteRangeTxn::CreateTxnsToDeleteContent(nsINode* aNode,
   1.183 +                                          int32_t aOffset,
   1.184 +                                          nsIEditor::EDirection aAction)
   1.185 +{
   1.186 +  // see what kind of node we have
   1.187 +  if (aNode->IsNodeOfType(nsINode::eDATA_NODE)) {
   1.188 +    // if the node is a chardata node, then delete chardata content
   1.189 +    uint32_t start, numToDelete;
   1.190 +    if (nsIEditor::eNext == aAction) {
   1.191 +      start = aOffset;
   1.192 +      numToDelete = aNode->Length() - aOffset;
   1.193 +    } else {
   1.194 +      start = 0;
   1.195 +      numToDelete = aOffset;
   1.196 +    }
   1.197 +
   1.198 +    if (numToDelete) {
   1.199 +      nsRefPtr<DeleteTextTxn> txn = new DeleteTextTxn();
   1.200 +
   1.201 +      nsCOMPtr<nsIDOMCharacterData> charDataNode = do_QueryInterface(aNode);
   1.202 +      nsresult res = txn->Init(mEditor, charDataNode, start, numToDelete,
   1.203 +                               mRangeUpdater);
   1.204 +      NS_ENSURE_SUCCESS(res, res);
   1.205 +
   1.206 +      AppendChild(txn);
   1.207 +    }
   1.208 +  }
   1.209 +
   1.210 +  return NS_OK;
   1.211 +}
   1.212 +
   1.213 +nsresult
   1.214 +DeleteRangeTxn::CreateTxnsToDeleteNodesBetween()
   1.215 +{
   1.216 +  nsCOMPtr<nsIContentIterator> iter = NS_NewContentSubtreeIterator();
   1.217 +
   1.218 +  nsresult res = iter->Init(mRange);
   1.219 +  NS_ENSURE_SUCCESS(res, res);
   1.220 +
   1.221 +  while (!iter->IsDone()) {
   1.222 +    nsCOMPtr<nsINode> node = iter->GetCurrentNode();
   1.223 +    NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
   1.224 +
   1.225 +    nsRefPtr<DeleteNodeTxn> txn = new DeleteNodeTxn();
   1.226 +
   1.227 +    res = txn->Init(mEditor, node, mRangeUpdater);
   1.228 +    NS_ENSURE_SUCCESS(res, res);
   1.229 +    AppendChild(txn);
   1.230 +
   1.231 +    iter->Next();
   1.232 +  }
   1.233 +  return NS_OK;
   1.234 +}

mercurial