Back to index

lightning-sunbird  0.9+nobinonly
InsertTextTxn.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
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "InsertTextTxn.h"
00039 #include "nsIDOMCharacterData.h"
00040 #include "nsISelection.h"
00041 #include "EditAggregateTxn.h"
00042 
00043 #ifdef NS_DEBUG
00044 static PRBool gNoisy = PR_FALSE;
00045 #endif
00046 
00047 nsIAtom *InsertTextTxn::gInsertTextTxnName;
00048 
00049 nsresult InsertTextTxn::ClassInit()
00050 {
00051   if (!gInsertTextTxnName)
00052     gInsertTextTxnName = NS_NewAtom("NS_InsertTextTxn");
00053   return NS_OK;
00054 }
00055 
00056 nsresult InsertTextTxn::ClassShutdown()
00057 {
00058   NS_IF_RELEASE(gInsertTextTxnName);
00059   return NS_OK;
00060 }
00061 
00062 InsertTextTxn::InsertTextTxn()
00063   : EditTxn()
00064 {
00065 }
00066 
00067 InsertTextTxn::~InsertTextTxn()
00068 {
00069 }
00070 
00071 NS_IMETHODIMP InsertTextTxn::Init(nsIDOMCharacterData *aElement,
00072                                   PRUint32             aOffset,
00073                                   const nsAString     &aStringToInsert,
00074                                   nsIEditor           *aEditor)
00075 {
00076 #if 0
00077       nsAutoString text;
00078       aElement->GetData(text);
00079       printf("InsertTextTxn: Offset to insert at = %d. Text of the node to insert into:\n", aOffset);
00080       wprintf(text.get());
00081       printf("\n");
00082 #endif
00083 
00084   NS_ASSERTION(aElement && aEditor, "bad args");
00085   if (!aElement || !aEditor) return NS_ERROR_NULL_POINTER;
00086 
00087   mElement = do_QueryInterface(aElement);
00088   mOffset = aOffset;
00089   mStringToInsert = aStringToInsert;
00090   mEditor = aEditor;
00091   return NS_OK;
00092 }
00093 
00094 NS_IMETHODIMP InsertTextTxn::DoTransaction(void)
00095 {
00096 #ifdef NS_DEBUG
00097   if (gNoisy) { printf("Do Insert Text element = %p\n", mElement.get()); }
00098 #endif
00099 
00100   NS_ASSERTION(mElement && mEditor, "bad state");
00101   if (!mElement || !mEditor) { return NS_ERROR_NOT_INITIALIZED; }
00102 
00103   nsresult result = mElement->InsertData(mOffset, mStringToInsert);
00104   if (NS_FAILED(result)) return result;
00105 
00106   // only set selection to insertion point if editor gives permission
00107   PRBool bAdjustSelection;
00108   mEditor->ShouldTxnSetSelection(&bAdjustSelection);
00109   if (bAdjustSelection)
00110   {
00111     nsCOMPtr<nsISelection> selection;
00112     result = mEditor->GetSelection(getter_AddRefs(selection));
00113     if (NS_FAILED(result)) return result;
00114     if (!selection) return NS_ERROR_NULL_POINTER;
00115     result = selection->Collapse(mElement, mOffset+mStringToInsert.Length());
00116     NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after insert.");
00117   }
00118   else
00119   {
00120     // do nothing - dom range gravity will adjust selection
00121   }
00122 
00123   return result;
00124 }
00125 
00126 NS_IMETHODIMP InsertTextTxn::UndoTransaction(void)
00127 {
00128 #ifdef NS_DEBUG
00129   if (gNoisy) { printf("Undo Insert Text element = %p\n", mElement.get()); }
00130 #endif
00131 
00132   NS_ASSERTION(mElement && mEditor, "bad state");
00133   if (!mElement || !mEditor) { return NS_ERROR_NOT_INITIALIZED; }
00134 
00135   PRUint32 length = mStringToInsert.Length();
00136   return mElement->DeleteData(mOffset, length);
00137 }
00138 
00139 NS_IMETHODIMP InsertTextTxn::Merge(nsITransaction *aTransaction, PRBool *aDidMerge)
00140 {
00141   // set out param default value
00142   if (aDidMerge)
00143     *aDidMerge = PR_FALSE;
00144   nsresult result = NS_OK;
00145   if (aDidMerge && aTransaction)
00146   {
00147     // if aTransaction is a InsertTextTxn, and if the selection hasn't changed, 
00148     // then absorb it
00149     InsertTextTxn *otherInsTxn = nsnull;
00150     aTransaction->QueryInterface(InsertTextTxn::GetCID(), (void **)&otherInsTxn);
00151     if (otherInsTxn)
00152     {
00153       if (IsSequentialInsert(otherInsTxn))
00154       {
00155         nsAutoString otherData;
00156         otherInsTxn->GetData(otherData);
00157         mStringToInsert += otherData;
00158         *aDidMerge = PR_TRUE;
00159 #ifdef NS_DEBUG
00160         if (gNoisy) { printf("InsertTextTxn assimilated %p\n", aTransaction); }
00161 #endif
00162       }
00163       NS_RELEASE(otherInsTxn);
00164     }
00165     else
00166     { // the next InsertTextTxn might be inside an aggregate that we have special knowledge of
00167       EditAggregateTxn *otherTxn = nsnull;
00168       aTransaction->QueryInterface(EditAggregateTxn::GetCID(), (void **)&otherTxn);
00169       if (otherTxn)
00170       {
00171         nsCOMPtr<nsIAtom> txnName;
00172         otherTxn->GetName(getter_AddRefs(txnName));
00173         if (txnName && txnName.get()==gInsertTextTxnName)
00174         { // yep, it's one of ours.  By definition, it must contain only
00175           // another aggregate with a single child,
00176           // or a single InsertTextTxn
00177           EditTxn * childTxn;
00178           otherTxn->GetTxnAt(0, (&childTxn));
00179           if (childTxn)
00180           {
00181             InsertTextTxn * otherInsertTxn = nsnull;
00182             result = childTxn->QueryInterface(InsertTextTxn::GetCID(), (void**)&otherInsertTxn);
00183             if (NS_SUCCEEDED(result))
00184             {
00185               if (otherInsertTxn)
00186               {
00187                 if (IsSequentialInsert(otherInsertTxn))
00188                      {
00189                        nsAutoString otherData;
00190                        otherInsertTxn->GetData(otherData);
00191                        mStringToInsert += otherData;
00192                        *aDidMerge = PR_TRUE;
00193 #ifdef NS_DEBUG
00194                        if (gNoisy) { printf("InsertTextTxn assimilated %p\n", aTransaction); }
00195 #endif
00196                      }
00197                      NS_RELEASE(otherInsertTxn);
00198                    }
00199             }
00200             
00201             NS_RELEASE(childTxn);
00202           }
00203         }
00204         NS_RELEASE(otherTxn);
00205       }
00206     }
00207   }
00208   return result;
00209 }
00210 
00211 NS_IMETHODIMP InsertTextTxn::GetTxnDescription(nsAString& aString)
00212 {
00213   aString.AssignLiteral("InsertTextTxn: ");
00214   aString += mStringToInsert;
00215   return NS_OK;
00216 }
00217 
00218 /* ============= nsISupports implementation ====================== */
00219 
00220 NS_IMETHODIMP
00221 InsertTextTxn::QueryInterface(REFNSIID aIID, void** aInstancePtr)
00222 {
00223   if (!aInstancePtr) {
00224     return NS_ERROR_NULL_POINTER;
00225   }
00226   if (aIID.Equals(InsertTextTxn::GetCID())) {
00227     *aInstancePtr = (void*)(InsertTextTxn*)this;
00228     NS_ADDREF_THIS();
00229     return NS_OK;
00230   }
00231   return (EditTxn::QueryInterface(aIID, aInstancePtr));
00232 }
00233 
00234 /* ============ protected methods ================== */
00235 
00236 NS_IMETHODIMP InsertTextTxn::GetData(nsString& aResult)
00237 {
00238   aResult = mStringToInsert;
00239   return NS_OK;
00240 }
00241 
00242 PRBool InsertTextTxn::IsSequentialInsert(InsertTextTxn *aOtherTxn)
00243 {
00244   NS_ASSERTION(aOtherTxn, "null param");
00245   if (aOtherTxn && aOtherTxn->mElement == mElement)
00246   {
00247     // here, we need to compare offsets.
00248     PRInt32 length = mStringToInsert.Length();
00249     if (aOtherTxn->mOffset == (mOffset + length))
00250       return PR_TRUE;
00251   }
00252   return PR_FALSE;
00253 }