Back to index

lightning-sunbird  0.9+nobinonly
IMETextTxn.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "IMETextTxn.h"
00040 #include "nsIDOMCharacterData.h"
00041 #include "nsIDOMRange.h"
00042 #include "nsIPrivateTextRange.h"
00043 #include "nsISelection.h"
00044 #include "nsISelectionPrivate.h"
00045 #include "nsISelectionController.h"
00046 #include "nsComponentManagerUtils.h"
00047 
00048 // #define DEBUG_IMETXN
00049 
00050 nsIAtom *IMETextTxn::gIMETextTxnName = nsnull;
00051 
00052 nsresult IMETextTxn::ClassInit()
00053 {
00054   if (!gIMETextTxnName)
00055     gIMETextTxnName = NS_NewAtom("NS_IMETextTxn");
00056   return NS_OK;
00057 }
00058 
00059 nsresult IMETextTxn::ClassShutdown()
00060 {
00061   NS_IF_RELEASE(gIMETextTxnName);
00062   return NS_OK;
00063 }
00064 
00065 IMETextTxn::IMETextTxn()
00066   : EditTxn()
00067 {
00068 }
00069 
00070 IMETextTxn::~IMETextTxn()
00071 {
00072   mRangeList = do_QueryInterface(nsnull);
00073 }
00074 
00075 NS_IMETHODIMP IMETextTxn::Init(nsIDOMCharacterData     *aElement,
00076                                PRUint32                 aOffset,
00077                                PRUint32                 aReplaceLength,
00078                                nsIPrivateTextRangeList *aTextRangeList,
00079                                const nsAString         &aStringToInsert,
00080                                nsWeakPtr                aSelConWeak)
00081 {
00082   NS_ASSERTION(aElement, "illegal value- null ptr- aElement");
00083   NS_ASSERTION(aTextRangeList, "illegal value- null ptr - aTextRangeList");
00084   if (!aElement || !aTextRangeList)
00085      return NS_ERROR_NULL_POINTER;
00086   mElement = do_QueryInterface(aElement);
00087   mOffset = aOffset;
00088   mReplaceLength = aReplaceLength;
00089   mStringToInsert = aStringToInsert;
00090   mSelConWeak = aSelConWeak;
00091   mRangeList = do_QueryInterface(aTextRangeList);
00092   mFixed = PR_FALSE;
00093   return NS_OK;
00094 }
00095 
00096 NS_IMETHODIMP IMETextTxn::DoTransaction(void)
00097 {
00098 
00099 #ifdef DEBUG_IMETXN
00100   printf("Do IME Text element = %p replace = %d len = %d\n", mElement.get(), mReplaceLength, mStringToInsert.Length());
00101 #endif
00102 
00103   nsCOMPtr<nsISelectionController> selCon = do_QueryReferent(mSelConWeak);
00104   if (!selCon) return NS_ERROR_NOT_INITIALIZED;
00105 
00106   // advance caret: This requires the presentation shell to get the selection.
00107   nsresult result;
00108   if (mReplaceLength == 0) {
00109     result = mElement->InsertData(mOffset, mStringToInsert);
00110   } else {
00111     result = mElement->ReplaceData(mOffset, mReplaceLength, mStringToInsert);
00112   }
00113   if (NS_SUCCEEDED(result)) {
00114     result = CollapseTextSelection();
00115   }
00116 
00117   return result;
00118 }
00119 
00120 NS_IMETHODIMP IMETextTxn::UndoTransaction(void)
00121 {
00122 #ifdef DEBUG_IMETXN
00123   printf("Undo IME Text element = %p\n", mElement.get());
00124 #endif
00125 
00126   nsCOMPtr<nsISelectionController> selCon = do_QueryReferent(mSelConWeak);
00127   if (!selCon) return NS_ERROR_NOT_INITIALIZED;
00128 
00129   nsresult result = mElement->DeleteData(mOffset, mStringToInsert.Length());
00130   if (NS_SUCCEEDED(result))
00131   { // set the selection to the insertion point where the string was removed
00132     nsCOMPtr<nsISelection> selection;
00133     result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
00134     if (NS_SUCCEEDED(result) && selection) {
00135       result = selection->Collapse(mElement, mOffset);
00136       NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after undo of IME insert.");
00137     }
00138   }
00139   return result;
00140 }
00141 
00142 NS_IMETHODIMP IMETextTxn::Merge(nsITransaction *aTransaction, PRBool *aDidMerge)
00143 {
00144   NS_ASSERTION(aDidMerge, "illegal vaule- null ptr- aDidMerge");
00145   NS_ASSERTION(aTransaction, "illegal vaule- null ptr- aTransaction");
00146   if (!aDidMerge || !aTransaction)
00147     return NS_ERROR_NULL_POINTER;
00148     
00149 #ifdef DEBUG_IMETXN
00150   printf("Merge IME Text element = %p\n", mElement.get());
00151 #endif
00152 
00153   // 
00154   // check to make sure we aren't fixed, if we are then nothing get's absorbed
00155   //
00156   if (mFixed) {
00157     *aDidMerge = PR_FALSE;
00158     return NS_OK;
00159   }
00160 
00161   //
00162   // if aTransaction is another IMETextTxn then absorb it
00163   //
00164   IMETextTxn*  otherTxn = nsnull;
00165   nsresult result = aTransaction->QueryInterface(IMETextTxn::GetCID(),(void**)&otherTxn);
00166   if (otherTxn && NS_SUCCEEDED(result))
00167   {
00168     //
00169     //  we absorb the next IME transaction by adopting its insert string as our own
00170     //
00171     nsIPrivateTextRangeList* newTextRangeList;
00172     otherTxn->GetData(mStringToInsert,&newTextRangeList);
00173     mRangeList = do_QueryInterface(newTextRangeList);
00174     *aDidMerge = PR_TRUE;
00175 #ifdef DEBUG_IMETXN
00176     printf("IMETextTxn assimilated IMETextTxn:%p\n", aTransaction);
00177 #endif
00178     NS_RELEASE(otherTxn);
00179     return NS_OK;
00180   }
00181 
00182   *aDidMerge = PR_FALSE;
00183   return NS_OK;
00184 }
00185 
00186 NS_IMETHODIMP IMETextTxn::MarkFixed(void)
00187 {
00188   mFixed = PR_TRUE;
00189   return NS_OK;
00190 }
00191 
00192 NS_IMETHODIMP IMETextTxn::GetTxnDescription(nsAString& aString)
00193 {
00194   aString.AssignLiteral("IMETextTxn: ");
00195   aString += mStringToInsert;
00196   return NS_OK;
00197 }
00198 
00199 /* ============= nsISupports implementation ====================== */
00200 
00201 NS_IMETHODIMP
00202 IMETextTxn::QueryInterface(REFNSIID aIID, void** aInstancePtr)
00203 {
00204   if (nsnull == aInstancePtr) {
00205     return NS_ERROR_NULL_POINTER;
00206   }
00207   if (aIID.Equals(IMETextTxn::GetCID())) {
00208     *aInstancePtr = (void*)(IMETextTxn*)this;
00209     NS_ADDREF_THIS();
00210     return NS_OK;
00211   }
00212   return (EditTxn::QueryInterface(aIID, aInstancePtr));
00213 }
00214 
00215 /* ============ protected methods ================== */
00216 static SelectionType TextRangeToSelection(int aTextRangeType)
00217 {
00218    switch(aTextRangeType)
00219    {
00220       case nsIPrivateTextRange::TEXTRANGE_RAWINPUT:
00221            return nsISelectionController::SELECTION_IME_RAWINPUT;
00222       case nsIPrivateTextRange::TEXTRANGE_SELECTEDRAWTEXT:
00223            return nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT;
00224       case nsIPrivateTextRange::TEXTRANGE_CONVERTEDTEXT:
00225            return nsISelectionController::SELECTION_IME_CONVERTEDTEXT;
00226       case nsIPrivateTextRange::TEXTRANGE_SELECTEDCONVERTEDTEXT:
00227            return nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT;
00228       case nsIPrivateTextRange::TEXTRANGE_CARETPOSITION:
00229       default:
00230            return nsISelectionController::SELECTION_NORMAL;
00231    };
00232 }
00233 
00234 NS_IMETHODIMP IMETextTxn::GetData(nsString& aResult,nsIPrivateTextRangeList** aTextRangeList)
00235 {
00236   NS_ASSERTION(aTextRangeList, "illegal value- null ptr- aTextRangeList");
00237   if (!aTextRangeList)
00238     return NS_ERROR_NULL_POINTER;
00239   aResult = mStringToInsert;
00240   *aTextRangeList = mRangeList;
00241   return NS_OK;
00242 }
00243 
00244 static SelectionType sel[4]=
00245 {
00246   nsISelectionController::SELECTION_IME_RAWINPUT,
00247   nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT,
00248   nsISelectionController::SELECTION_IME_CONVERTEDTEXT,
00249   nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT
00250 };
00251 
00252 NS_IMETHODIMP IMETextTxn::CollapseTextSelection(void)
00253 {
00254     nsresult      result;
00255     PRUint16      i;
00256 
00257 #ifdef DEBUG_IMETXN
00258     PRUint16 listlen,start,stop,type;
00259     result = mRangeList->GetLength(&listlen);
00260     printf("nsIPrivateTextRangeList[%p]\n",mRangeList);
00261     nsIPrivateTextRange* rangePtr;
00262     for (i=0;i<listlen;i++) {
00263       (void)mRangeList->Item(i,&rangePtr);
00264       rangePtr->GetRangeStart(&start);
00265       rangePtr->GetRangeEnd(&stop);
00266       rangePtr->GetRangeType(&type);
00267       printf("range[%d] start=%d end=%d type=",i,start,stop,type);
00268       if (type==nsIPrivateTextRange::TEXTRANGE_RAWINPUT)
00269                              printf("TEXTRANGE_RAWINPUT\n");
00270       else if (type==nsIPrivateTextRange::TEXTRANGE_SELECTEDRAWTEXT)
00271                                   printf("TEXTRANGE_SELECTEDRAWTEXT\n");
00272       else if (type==nsIPrivateTextRange::TEXTRANGE_CONVERTEDTEXT)
00273                                   printf("TEXTRANGE_CONVERTEDTEXT\n");
00274       else if (type==nsIPrivateTextRange::TEXTRANGE_SELECTEDCONVERTEDTEXT)
00275                                   printf("TEXTRANGE_SELECTEDCONVERTEDTEXT\n");
00276       else if (type==nsIPrivateTextRange::TEXTRANGE_CARETPOSITION)
00277                                   printf("TEXTRANGE_CARETPOSITION\n");
00278       else printf("unknown constant\n");
00279     }
00280 #endif
00281         
00282     //
00283     // run through the text range list, if any
00284     //
00285     nsCOMPtr<nsISelectionController> selCon = do_QueryReferent(mSelConWeak);
00286     if (!selCon) return NS_ERROR_NOT_INITIALIZED;
00287 
00288     PRUint16      textRangeListLength,selectionStart,selectionEnd,
00289                   textRangeType;
00290     
00291     result = mRangeList->GetLength(&textRangeListLength);
00292     if(NS_FAILED(result))
00293         return result;
00294     nsCOMPtr<nsISelection> selection;
00295     result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
00296     if(NS_SUCCEEDED(result))
00297     {
00298       nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
00299       result = selPriv->StartBatchChanges();
00300       if (NS_SUCCEEDED(result))
00301       {
00302         nsCOMPtr<nsISelection> imeSel;
00303         for(PRInt8 selIdx = 0; selIdx < 4;selIdx++)
00304         {
00305           result = selCon->GetSelection(sel[selIdx], getter_AddRefs(imeSel));
00306           if (NS_SUCCEEDED(result))
00307           {
00308             result = imeSel->RemoveAllRanges();
00309             NS_ASSERTION(NS_SUCCEEDED(result), "Cannot ClearSelection");
00310             // we just ignore the result and clean up the next one here
00311           }
00312         }
00313 
00314         nsIPrivateTextRange*  textRange;
00315         PRBool setCaret=PR_FALSE;
00316         for(i=0;i<textRangeListLength;i++)
00317         {
00318           result = mRangeList->Item(i,&textRange);
00319           NS_ASSERTION(NS_SUCCEEDED(result), "cannot get item");
00320           if(NS_FAILED(result))
00321                break;
00322 
00323           result = textRange->GetRangeType(&textRangeType);
00324           NS_ASSERTION(NS_SUCCEEDED(result), "cannot get range type");
00325           if(NS_FAILED(result))
00326                break;
00327 
00328           result = textRange->GetRangeStart(&selectionStart);
00329           NS_ASSERTION(NS_SUCCEEDED(result), "cannot get range start");
00330           if(NS_FAILED(result))
00331                break;
00332           result = textRange->GetRangeEnd(&selectionEnd);
00333           NS_ASSERTION(NS_SUCCEEDED(result), "cannot get range end");
00334           if(NS_FAILED(result))
00335                break;
00336 
00337           if(nsIPrivateTextRange::TEXTRANGE_CARETPOSITION == textRangeType)
00338           {
00339              // Set the caret....
00340              result = selection->Collapse(mElement,
00341                       mOffset+selectionStart);
00342              NS_ASSERTION(NS_SUCCEEDED(result), "Cannot Collapse");
00343              if(NS_SUCCEEDED(result))
00344              setCaret = PR_TRUE;
00345           } else {
00346              // NS_ASSERTION(selectionStart != selectionEnd, "end == start");
00347              if(selectionStart == selectionEnd)
00348                 continue;
00349 
00350              result= selCon->GetSelection(TextRangeToSelection(textRangeType),
00351                      getter_AddRefs(imeSel));
00352              NS_ASSERTION(NS_SUCCEEDED(result), "Cannot get selction");
00353              if(NS_FAILED(result))
00354                 break;
00355 
00356              nsCOMPtr<nsIDOMRange> newRange = do_CreateInstance(
00357                                       "@mozilla.org/content/range;1", &result);
00358              NS_ASSERTION(NS_SUCCEEDED(result), "Cannot create new nsIDOMRange");
00359              if(NS_FAILED(result))
00360                 break;
00361 
00362              newRange->SetStart(mElement,mOffset+selectionStart);
00363              NS_ASSERTION(NS_SUCCEEDED(result), "Cannot SetStart");
00364              if(NS_FAILED(result))
00365                 break;
00366 
00367              newRange->SetEnd(mElement,mOffset+selectionEnd);
00368              NS_ASSERTION(NS_SUCCEEDED(result), "Cannot SetEnd");
00369              if(NS_FAILED(result))
00370                 break;
00371 
00372              imeSel->AddRange(newRange);
00373              NS_ASSERTION(NS_SUCCEEDED(result), "Cannot AddRange");
00374              if(NS_FAILED(result))
00375                 break;
00376 
00377           } // if GetRangeEnd
00378         } // for textRangeListLength
00379         if(! setCaret) {
00380           // set cursor
00381           result = selection->Collapse(mElement,mOffset+mStringToInsert.Length());
00382           NS_ASSERTION(NS_SUCCEEDED(result), "Cannot Collapse");
00383         }
00384         result = selPriv->EndBatchChanges();
00385         NS_ASSERTION(NS_SUCCEEDED(result), "Cannot EndBatchChanges");
00386       } // if StartBatchChanges
00387     } // if GetSelection
00388 
00389     return result;
00390 }
00391