editor/libeditor/base/IMETextTxn.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/editor/libeditor/base/IMETextTxn.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,301 @@
     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 "IMETextTxn.h"
    1.10 +#include "mozilla/DebugOnly.h"          // for DebugOnly
    1.11 +#include "mozilla/mozalloc.h"           // for operator new
    1.12 +#include "mozilla/TextEvents.h"         // for TextRangeStyle
    1.13 +#include "nsAString.h"                  // for nsAString_internal::Length, etc
    1.14 +#include "nsAutoPtr.h"                  // for nsRefPtr
    1.15 +#include "nsDebug.h"                    // for NS_ASSERTION, etc
    1.16 +#include "nsError.h"                    // for NS_SUCCEEDED, NS_FAILED, etc
    1.17 +#include "nsIDOMCharacterData.h"        // for nsIDOMCharacterData
    1.18 +#include "nsIDOMRange.h"                // for nsRange::SetEnd, etc
    1.19 +#include "nsIContent.h"                 // for nsIContent
    1.20 +#include "nsIEditor.h"                  // for nsIEditor
    1.21 +#include "nsIPresShell.h"               // for SelectionType
    1.22 +#include "nsISelection.h"               // for nsISelection
    1.23 +#include "nsISelectionController.h"     // for nsISelectionController, etc
    1.24 +#include "nsISelectionPrivate.h"        // for nsISelectionPrivate
    1.25 +#include "nsISupportsImpl.h"            // for nsRange::AddRef, etc
    1.26 +#include "nsISupportsUtils.h"           // for NS_ADDREF_THIS, NS_RELEASE
    1.27 +#include "nsITransaction.h"             // for nsITransaction
    1.28 +#include "nsRange.h"                    // for nsRange
    1.29 +#include "nsString.h"                   // for nsString
    1.30 +
    1.31 +using namespace mozilla;
    1.32 +
    1.33 +// #define DEBUG_IMETXN
    1.34 +
    1.35 +IMETextTxn::IMETextTxn()
    1.36 +  : EditTxn()
    1.37 +{
    1.38 +}
    1.39 +
    1.40 +NS_IMPL_CYCLE_COLLECTION_INHERITED(IMETextTxn, EditTxn,
    1.41 +                                   mElement)
    1.42 +// mRangeList can't lead to cycles
    1.43 +
    1.44 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IMETextTxn)
    1.45 +  if (aIID.Equals(IMETextTxn::GetCID())) {
    1.46 +    *aInstancePtr = (void*)(IMETextTxn*)this;
    1.47 +    NS_ADDREF_THIS();
    1.48 +    return NS_OK;
    1.49 +  } else
    1.50 +NS_INTERFACE_MAP_END_INHERITING(EditTxn)
    1.51 +
    1.52 +NS_IMETHODIMP IMETextTxn::Init(nsIDOMCharacterData     *aElement,
    1.53 +                               uint32_t                 aOffset,
    1.54 +                               uint32_t                 aReplaceLength,
    1.55 +                               TextRangeArray          *aTextRangeArray,
    1.56 +                               const nsAString         &aStringToInsert,
    1.57 +                               nsIEditor               *aEditor)
    1.58 +{
    1.59 +  NS_ENSURE_ARG_POINTER(aElement);
    1.60 +  mElement = aElement;
    1.61 +  mOffset = aOffset;
    1.62 +  mReplaceLength = aReplaceLength;
    1.63 +  mStringToInsert = aStringToInsert;
    1.64 +  mEditor = aEditor;
    1.65 +  mRanges = aTextRangeArray;
    1.66 +  mFixed = false;
    1.67 +  return NS_OK;
    1.68 +}
    1.69 +
    1.70 +NS_IMETHODIMP IMETextTxn::DoTransaction(void)
    1.71 +{
    1.72 +
    1.73 +#ifdef DEBUG_IMETXN
    1.74 +  printf("Do IME Text element = %p replace = %d len = %d\n", mElement.get(), mReplaceLength, mStringToInsert.Length());
    1.75 +#endif
    1.76 +
    1.77 +  nsCOMPtr<nsISelectionController> selCon;
    1.78 +  mEditor->GetSelectionController(getter_AddRefs(selCon));
    1.79 +  NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
    1.80 +
    1.81 +  // advance caret: This requires the presentation shell to get the selection.
    1.82 +  nsresult result;
    1.83 +  if (mReplaceLength == 0) {
    1.84 +    result = mElement->InsertData(mOffset, mStringToInsert);
    1.85 +  } else {
    1.86 +    result = mElement->ReplaceData(mOffset, mReplaceLength, mStringToInsert);
    1.87 +  }
    1.88 +  if (NS_SUCCEEDED(result)) {
    1.89 +    result = SetSelectionForRanges();
    1.90 +  }
    1.91 +
    1.92 +  return result;
    1.93 +}
    1.94 +
    1.95 +NS_IMETHODIMP IMETextTxn::UndoTransaction(void)
    1.96 +{
    1.97 +#ifdef DEBUG_IMETXN
    1.98 +  printf("Undo IME Text element = %p\n", mElement.get());
    1.99 +#endif
   1.100 +
   1.101 +  nsCOMPtr<nsISelectionController> selCon;
   1.102 +  mEditor->GetSelectionController(getter_AddRefs(selCon));
   1.103 +  NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
   1.104 +
   1.105 +  nsresult result = mElement->DeleteData(mOffset, mStringToInsert.Length());
   1.106 +  if (NS_SUCCEEDED(result))
   1.107 +  { // set the selection to the insertion point where the string was removed
   1.108 +    nsCOMPtr<nsISelection> selection;
   1.109 +    result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
   1.110 +    if (NS_SUCCEEDED(result) && selection) {
   1.111 +      result = selection->Collapse(mElement, mOffset);
   1.112 +      NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after undo of IME insert.");
   1.113 +    }
   1.114 +  }
   1.115 +  return result;
   1.116 +}
   1.117 +
   1.118 +NS_IMETHODIMP IMETextTxn::Merge(nsITransaction *aTransaction, bool *aDidMerge)
   1.119 +{
   1.120 +  NS_ASSERTION(aDidMerge, "illegal vaule- null ptr- aDidMerge");
   1.121 +  NS_ASSERTION(aTransaction, "illegal vaule- null ptr- aTransaction");
   1.122 +  NS_ENSURE_TRUE(aDidMerge && aTransaction, NS_ERROR_NULL_POINTER);
   1.123 +    
   1.124 +#ifdef DEBUG_IMETXN
   1.125 +  printf("Merge IME Text element = %p\n", mElement.get());
   1.126 +#endif
   1.127 +
   1.128 +  // 
   1.129 +  // check to make sure we aren't fixed, if we are then nothing get's absorbed
   1.130 +  //
   1.131 +  if (mFixed) {
   1.132 +    *aDidMerge = false;
   1.133 +    return NS_OK;
   1.134 +  }
   1.135 +
   1.136 +  //
   1.137 +  // if aTransaction is another IMETextTxn then absorb it
   1.138 +  //
   1.139 +  IMETextTxn*  otherTxn = nullptr;
   1.140 +  nsresult result = aTransaction->QueryInterface(IMETextTxn::GetCID(),(void**)&otherTxn);
   1.141 +  if (otherTxn && NS_SUCCEEDED(result))
   1.142 +  {
   1.143 +    //
   1.144 +    //  we absorb the next IME transaction by adopting its insert string as our own
   1.145 +    //
   1.146 +    mStringToInsert = otherTxn->mStringToInsert;
   1.147 +    mRanges = otherTxn->mRanges;
   1.148 +    *aDidMerge = true;
   1.149 +#ifdef DEBUG_IMETXN
   1.150 +    printf("IMETextTxn assimilated IMETextTxn:%p\n", aTransaction);
   1.151 +#endif
   1.152 +    NS_RELEASE(otherTxn);
   1.153 +    return NS_OK;
   1.154 +  }
   1.155 +
   1.156 +  *aDidMerge = false;
   1.157 +  return NS_OK;
   1.158 +}
   1.159 +
   1.160 +NS_IMETHODIMP IMETextTxn::MarkFixed(void)
   1.161 +{
   1.162 +  mFixed = true;
   1.163 +  return NS_OK;
   1.164 +}
   1.165 +
   1.166 +NS_IMETHODIMP IMETextTxn::GetTxnDescription(nsAString& aString)
   1.167 +{
   1.168 +  aString.AssignLiteral("IMETextTxn: ");
   1.169 +  aString += mStringToInsert;
   1.170 +  return NS_OK;
   1.171 +}
   1.172 +
   1.173 +/* ============ protected methods ================== */
   1.174 +static SelectionType
   1.175 +ToSelectionType(uint32_t aTextRangeType)
   1.176 +{
   1.177 +  switch(aTextRangeType) {
   1.178 +    case NS_TEXTRANGE_RAWINPUT:
   1.179 +      return nsISelectionController::SELECTION_IME_RAWINPUT;
   1.180 +    case NS_TEXTRANGE_SELECTEDRAWTEXT:
   1.181 +      return nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT;
   1.182 +    case NS_TEXTRANGE_CONVERTEDTEXT:
   1.183 +      return nsISelectionController::SELECTION_IME_CONVERTEDTEXT;
   1.184 +    case NS_TEXTRANGE_SELECTEDCONVERTEDTEXT:
   1.185 +      return nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT;
   1.186 +    default:
   1.187 +      MOZ_CRASH("Selection type is invalid");
   1.188 +      return nsISelectionController::SELECTION_NORMAL;
   1.189 +  }
   1.190 +}
   1.191 +
   1.192 +nsresult
   1.193 +IMETextTxn::SetSelectionForRanges()
   1.194 +{
   1.195 +  nsCOMPtr<nsISelectionController> selCon;
   1.196 +  mEditor->GetSelectionController(getter_AddRefs(selCon));
   1.197 +  NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
   1.198 +
   1.199 +  nsCOMPtr<nsISelection> selection;
   1.200 +  nsresult rv =
   1.201 +    selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
   1.202 +                         getter_AddRefs(selection));
   1.203 +  NS_ENSURE_SUCCESS(rv, rv);
   1.204 +
   1.205 +  nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
   1.206 +  rv = selPriv->StartBatchChanges();
   1.207 +  NS_ENSURE_SUCCESS(rv, rv);
   1.208 +
   1.209 +  // First, remove all selections of IME composition.
   1.210 +  static const SelectionType kIMESelections[] = {
   1.211 +    nsISelectionController::SELECTION_IME_RAWINPUT,
   1.212 +    nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT,
   1.213 +    nsISelectionController::SELECTION_IME_CONVERTEDTEXT,
   1.214 +    nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT
   1.215 +  };
   1.216 +  for (uint32_t i = 0; i < ArrayLength(kIMESelections); ++i) {
   1.217 +    nsCOMPtr<nsISelection> selectionOfIME;
   1.218 +    if (NS_FAILED(selCon->GetSelection(kIMESelections[i],
   1.219 +                                       getter_AddRefs(selectionOfIME)))) {
   1.220 +      continue;
   1.221 +    }
   1.222 +    DebugOnly<nsresult> rv = selectionOfIME->RemoveAllRanges();
   1.223 +    NS_ASSERTION(NS_SUCCEEDED(rv),
   1.224 +                 "Failed to remove all ranges of IME selection");
   1.225 +  }
   1.226 +
   1.227 +  // Set caret position and selection of IME composition with TextRangeArray.
   1.228 +  bool setCaret = false;
   1.229 +  uint32_t countOfRanges = mRanges ? mRanges->Length() : 0;
   1.230 +  for (uint32_t i = 0; i < countOfRanges; ++i) {
   1.231 +    const TextRange& textRange = mRanges->ElementAt(i);
   1.232 +
   1.233 +    // Caret needs special handling since its length may be 0 and if it's not
   1.234 +    // specified explicitly, we need to handle it ourselves later.
   1.235 +    if (textRange.mRangeType == NS_TEXTRANGE_CARETPOSITION) {
   1.236 +      NS_ASSERTION(!setCaret, "The ranges already has caret position");
   1.237 +      NS_ASSERTION(!textRange.Length(), "nsEditor doesn't support wide caret");
   1.238 +      // NOTE: If the caret position is larger than max length of the editor
   1.239 +      //       content, this may fail.
   1.240 +      rv = selection->Collapse(mElement, mOffset + textRange.mStartOffset);
   1.241 +      setCaret = setCaret || NS_SUCCEEDED(rv);
   1.242 +      NS_ASSERTION(setCaret, "Failed to collapse normal selection");
   1.243 +      continue;
   1.244 +    }
   1.245 +
   1.246 +    // If the clause length is 0, it's should be a bug.
   1.247 +    if (!textRange.Length()) {
   1.248 +      NS_WARNING("Any clauses must not be empty");
   1.249 +      continue;
   1.250 +    }
   1.251 +
   1.252 +    nsRefPtr<nsRange> clauseRange;
   1.253 +    rv = nsRange::CreateRange(mElement, mOffset + textRange.mStartOffset,
   1.254 +                              mElement, mOffset + textRange.mEndOffset,
   1.255 +                              getter_AddRefs(clauseRange));
   1.256 +    if (NS_FAILED(rv)) {
   1.257 +      NS_WARNING("Failed to create a DOM range for a clause of composition");
   1.258 +      break;
   1.259 +    }
   1.260 +
   1.261 +    // Set the range of the clause to selection.
   1.262 +    nsCOMPtr<nsISelection> selectionOfIME;
   1.263 +    rv = selCon->GetSelection(ToSelectionType(textRange.mRangeType),
   1.264 +                              getter_AddRefs(selectionOfIME));
   1.265 +    if (NS_FAILED(rv)) {
   1.266 +      NS_WARNING("Failed to get IME selection");
   1.267 +      break;
   1.268 +    }
   1.269 +
   1.270 +    rv = selectionOfIME->AddRange(clauseRange);
   1.271 +    if (NS_FAILED(rv)) {
   1.272 +      NS_WARNING("Failed to add selection range for a clause of composition");
   1.273 +      break;
   1.274 +    }
   1.275 +
   1.276 +    // Set the style of the clause.
   1.277 +    nsCOMPtr<nsISelectionPrivate> selectionOfIMEPriv =
   1.278 +                                    do_QueryInterface(selectionOfIME);
   1.279 +    if (!selectionOfIMEPriv) {
   1.280 +      NS_WARNING("Failed to get nsISelectionPrivate interface from selection");
   1.281 +      continue; // Since this is additional feature, we can continue this job.
   1.282 +    }
   1.283 +    rv = selectionOfIMEPriv->SetTextRangeStyle(clauseRange,
   1.284 +                                               textRange.mRangeStyle);
   1.285 +    if (NS_FAILED(rv)) {
   1.286 +      NS_WARNING("Failed to set selection style");
   1.287 +      break; // but this is unexpected...
   1.288 +    }
   1.289 +  }
   1.290 +
   1.291 +  // If the ranges doesn't include explicit caret position, let's set the
   1.292 +  // caret to the end of composition string.
   1.293 +  if (!setCaret) {
   1.294 +    rv = selection->Collapse(mElement, mOffset + mStringToInsert.Length());
   1.295 +    NS_ASSERTION(NS_SUCCEEDED(rv),
   1.296 +                 "Failed to set caret at the end of composition string");
   1.297 +  }
   1.298 +
   1.299 +  rv = selPriv->EndBatchChanges();
   1.300 +  NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to end batch changes");
   1.301 +
   1.302 +  return rv;
   1.303 +}
   1.304 +

mercurial