1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998-1999
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Pierre Phaneuf <pp@ludusdesign.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "IMETextTxn.h"
40 : #include "nsIDOMCharacterData.h"
41 : #include "nsRange.h"
42 : #include "nsIPrivateTextRange.h"
43 : #include "nsISelection.h"
44 : #include "nsISelectionPrivate.h"
45 : #include "nsISelectionController.h"
46 : #include "nsComponentManagerUtils.h"
47 : #include "nsIEditor.h"
48 :
49 : // #define DEBUG_IMETXN
50 :
51 0 : IMETextTxn::IMETextTxn()
52 0 : : EditTxn()
53 : {
54 0 : }
55 :
56 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(IMETextTxn)
57 :
58 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IMETextTxn, EditTxn)
59 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mElement)
60 : // mRangeList can't lead to cycles
61 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
62 :
63 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IMETextTxn, EditTxn)
64 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mElement)
65 : // mRangeList can't lead to cycles
66 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
67 :
68 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IMETextTxn)
69 0 : if (aIID.Equals(IMETextTxn::GetCID())) {
70 0 : *aInstancePtr = (void*)(IMETextTxn*)this;
71 0 : NS_ADDREF_THIS();
72 0 : return NS_OK;
73 : } else
74 0 : NS_INTERFACE_MAP_END_INHERITING(EditTxn)
75 :
76 0 : NS_IMETHODIMP IMETextTxn::Init(nsIDOMCharacterData *aElement,
77 : PRUint32 aOffset,
78 : PRUint32 aReplaceLength,
79 : nsIPrivateTextRangeList *aTextRangeList,
80 : const nsAString &aStringToInsert,
81 : nsIEditor *aEditor)
82 : {
83 0 : NS_ASSERTION(aElement, "illegal value- null ptr- aElement");
84 0 : NS_ASSERTION(aTextRangeList, "illegal value- null ptr - aTextRangeList");
85 0 : NS_ENSURE_TRUE(aElement && aTextRangeList, NS_ERROR_NULL_POINTER);
86 0 : mElement = do_QueryInterface(aElement);
87 0 : mOffset = aOffset;
88 0 : mReplaceLength = aReplaceLength;
89 0 : mStringToInsert = aStringToInsert;
90 0 : mEditor = aEditor;
91 0 : mRangeList = do_QueryInterface(aTextRangeList);
92 0 : mFixed = false;
93 0 : return NS_OK;
94 : }
95 :
96 0 : NS_IMETHODIMP IMETextTxn::DoTransaction(void)
97 : {
98 :
99 : #ifdef DEBUG_IMETXN
100 : printf("Do IME Text element = %p replace = %d len = %d\n", mElement.get(), mReplaceLength, mStringToInsert.Length());
101 : #endif
102 :
103 0 : nsCOMPtr<nsISelectionController> selCon;
104 0 : mEditor->GetSelectionController(getter_AddRefs(selCon));
105 0 : NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
106 :
107 : // advance caret: This requires the presentation shell to get the selection.
108 : nsresult result;
109 0 : if (mReplaceLength == 0) {
110 0 : result = mElement->InsertData(mOffset, mStringToInsert);
111 : } else {
112 0 : result = mElement->ReplaceData(mOffset, mReplaceLength, mStringToInsert);
113 : }
114 0 : if (NS_SUCCEEDED(result)) {
115 0 : result = CollapseTextSelection();
116 : }
117 :
118 0 : return result;
119 : }
120 :
121 0 : NS_IMETHODIMP IMETextTxn::UndoTransaction(void)
122 : {
123 : #ifdef DEBUG_IMETXN
124 : printf("Undo IME Text element = %p\n", mElement.get());
125 : #endif
126 :
127 0 : nsCOMPtr<nsISelectionController> selCon;
128 0 : mEditor->GetSelectionController(getter_AddRefs(selCon));
129 0 : NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
130 :
131 0 : nsresult result = mElement->DeleteData(mOffset, mStringToInsert.Length());
132 0 : if (NS_SUCCEEDED(result))
133 : { // set the selection to the insertion point where the string was removed
134 0 : nsCOMPtr<nsISelection> selection;
135 0 : result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
136 0 : if (NS_SUCCEEDED(result) && selection) {
137 0 : result = selection->Collapse(mElement, mOffset);
138 0 : NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after undo of IME insert.");
139 : }
140 : }
141 0 : return result;
142 : }
143 :
144 0 : NS_IMETHODIMP IMETextTxn::Merge(nsITransaction *aTransaction, bool *aDidMerge)
145 : {
146 0 : NS_ASSERTION(aDidMerge, "illegal vaule- null ptr- aDidMerge");
147 0 : NS_ASSERTION(aTransaction, "illegal vaule- null ptr- aTransaction");
148 0 : NS_ENSURE_TRUE(aDidMerge && aTransaction, NS_ERROR_NULL_POINTER);
149 :
150 : #ifdef DEBUG_IMETXN
151 : printf("Merge IME Text element = %p\n", mElement.get());
152 : #endif
153 :
154 : //
155 : // check to make sure we aren't fixed, if we are then nothing get's absorbed
156 : //
157 0 : if (mFixed) {
158 0 : *aDidMerge = false;
159 0 : return NS_OK;
160 : }
161 :
162 : //
163 : // if aTransaction is another IMETextTxn then absorb it
164 : //
165 0 : IMETextTxn* otherTxn = nsnull;
166 0 : nsresult result = aTransaction->QueryInterface(IMETextTxn::GetCID(),(void**)&otherTxn);
167 0 : if (otherTxn && NS_SUCCEEDED(result))
168 : {
169 : //
170 : // we absorb the next IME transaction by adopting its insert string as our own
171 : //
172 : nsIPrivateTextRangeList* newTextRangeList;
173 0 : otherTxn->GetData(mStringToInsert,&newTextRangeList);
174 0 : mRangeList = do_QueryInterface(newTextRangeList);
175 0 : *aDidMerge = true;
176 : #ifdef DEBUG_IMETXN
177 : printf("IMETextTxn assimilated IMETextTxn:%p\n", aTransaction);
178 : #endif
179 0 : NS_RELEASE(otherTxn);
180 0 : return NS_OK;
181 : }
182 :
183 0 : *aDidMerge = false;
184 0 : return NS_OK;
185 : }
186 :
187 0 : NS_IMETHODIMP IMETextTxn::MarkFixed(void)
188 : {
189 0 : mFixed = true;
190 0 : return NS_OK;
191 : }
192 :
193 0 : NS_IMETHODIMP IMETextTxn::GetTxnDescription(nsAString& aString)
194 : {
195 0 : aString.AssignLiteral("IMETextTxn: ");
196 0 : aString += mStringToInsert;
197 0 : return NS_OK;
198 : }
199 :
200 : /* ============ protected methods ================== */
201 0 : static SelectionType TextRangeToSelection(int aTextRangeType)
202 : {
203 0 : switch(aTextRangeType)
204 : {
205 : case nsIPrivateTextRange::TEXTRANGE_RAWINPUT:
206 0 : return nsISelectionController::SELECTION_IME_RAWINPUT;
207 : case nsIPrivateTextRange::TEXTRANGE_SELECTEDRAWTEXT:
208 0 : return nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT;
209 : case nsIPrivateTextRange::TEXTRANGE_CONVERTEDTEXT:
210 0 : return nsISelectionController::SELECTION_IME_CONVERTEDTEXT;
211 : case nsIPrivateTextRange::TEXTRANGE_SELECTEDCONVERTEDTEXT:
212 0 : return nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT;
213 : case nsIPrivateTextRange::TEXTRANGE_CARETPOSITION:
214 : default:
215 0 : return nsISelectionController::SELECTION_NORMAL;
216 : };
217 : }
218 :
219 0 : NS_IMETHODIMP IMETextTxn::GetData(nsString& aResult,nsIPrivateTextRangeList** aTextRangeList)
220 : {
221 0 : NS_ASSERTION(aTextRangeList, "illegal value- null ptr- aTextRangeList");
222 0 : NS_ENSURE_TRUE(aTextRangeList, NS_ERROR_NULL_POINTER);
223 0 : aResult = mStringToInsert;
224 0 : *aTextRangeList = mRangeList;
225 0 : return NS_OK;
226 : }
227 :
228 : static SelectionType sel[4]=
229 : {
230 : nsISelectionController::SELECTION_IME_RAWINPUT,
231 : nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT,
232 : nsISelectionController::SELECTION_IME_CONVERTEDTEXT,
233 : nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT
234 : };
235 :
236 0 : NS_IMETHODIMP IMETextTxn::CollapseTextSelection(void)
237 : {
238 : nsresult result;
239 : PRUint16 i;
240 :
241 : #ifdef DEBUG_IMETXN
242 : PRUint16 listlen,start,stop,type;
243 : result = mRangeList->GetLength(&listlen);
244 : printf("nsIPrivateTextRangeList[%p]\n",mRangeList);
245 : nsIPrivateTextRange* rangePtr;
246 : for (i=0;i<listlen;i++) {
247 : (void)mRangeList->Item(i,&rangePtr);
248 : rangePtr->GetRangeStart(&start);
249 : rangePtr->GetRangeEnd(&stop);
250 : rangePtr->GetRangeType(&type);
251 : printf("range[%d] start=%d end=%d type=",i,start,stop,type);
252 : if (type==nsIPrivateTextRange::TEXTRANGE_RAWINPUT)
253 : printf("TEXTRANGE_RAWINPUT\n");
254 : else if (type==nsIPrivateTextRange::TEXTRANGE_SELECTEDRAWTEXT)
255 : printf("TEXTRANGE_SELECTEDRAWTEXT\n");
256 : else if (type==nsIPrivateTextRange::TEXTRANGE_CONVERTEDTEXT)
257 : printf("TEXTRANGE_CONVERTEDTEXT\n");
258 : else if (type==nsIPrivateTextRange::TEXTRANGE_SELECTEDCONVERTEDTEXT)
259 : printf("TEXTRANGE_SELECTEDCONVERTEDTEXT\n");
260 : else if (type==nsIPrivateTextRange::TEXTRANGE_CARETPOSITION)
261 : printf("TEXTRANGE_CARETPOSITION\n");
262 : else printf("unknown constant\n");
263 : }
264 : #endif
265 :
266 : //
267 : // run through the text range list, if any
268 : //
269 0 : nsCOMPtr<nsISelectionController> selCon;
270 0 : mEditor->GetSelectionController(getter_AddRefs(selCon));
271 0 : NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
272 :
273 : PRUint16 textRangeListLength,selectionStart,selectionEnd,
274 : textRangeType;
275 :
276 0 : textRangeListLength = mRangeList->GetLength();
277 0 : nsCOMPtr<nsISelection> selection;
278 0 : result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
279 0 : if(NS_SUCCEEDED(result))
280 : {
281 0 : nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
282 0 : result = selPriv->StartBatchChanges();
283 0 : if (NS_SUCCEEDED(result))
284 : {
285 0 : nsCOMPtr<nsISelection> imeSel;
286 0 : for(PRInt8 selIdx = 0; selIdx < 4;selIdx++)
287 : {
288 0 : result = selCon->GetSelection(sel[selIdx], getter_AddRefs(imeSel));
289 0 : if (NS_SUCCEEDED(result))
290 : {
291 0 : result = imeSel->RemoveAllRanges();
292 0 : NS_ASSERTION(NS_SUCCEEDED(result), "Cannot ClearSelection");
293 : // we just ignore the result and clean up the next one here
294 : }
295 : }
296 :
297 0 : nsCOMPtr<nsIPrivateTextRange> textRange;
298 0 : bool setCaret=false;
299 0 : for(i=0;i<textRangeListLength;i++)
300 : {
301 0 : textRange = mRangeList->Item(i);
302 0 : NS_ASSERTION(textRange, "cannot get item");
303 0 : if(!textRange)
304 0 : break;
305 :
306 0 : result = textRange->GetRangeType(&textRangeType);
307 0 : NS_ASSERTION(NS_SUCCEEDED(result), "cannot get range type");
308 0 : if(NS_FAILED(result))
309 0 : break;
310 :
311 0 : result = textRange->GetRangeStart(&selectionStart);
312 0 : NS_ASSERTION(NS_SUCCEEDED(result), "cannot get range start");
313 0 : if(NS_FAILED(result))
314 0 : break;
315 0 : result = textRange->GetRangeEnd(&selectionEnd);
316 0 : NS_ASSERTION(NS_SUCCEEDED(result), "cannot get range end");
317 0 : if(NS_FAILED(result))
318 0 : break;
319 :
320 0 : if(nsIPrivateTextRange::TEXTRANGE_CARETPOSITION == textRangeType)
321 : {
322 0 : NS_ASSERTION(selectionStart == selectionEnd,
323 : "nsEditor doesn't support wide caret");
324 : // Set the caret....
325 0 : result = selection->Collapse(mElement,
326 0 : mOffset+selectionStart);
327 0 : NS_ASSERTION(NS_SUCCEEDED(result), "Cannot Collapse");
328 0 : if(NS_SUCCEEDED(result))
329 0 : setCaret = true;
330 : } else {
331 : // NS_ASSERTION(selectionStart != selectionEnd, "end == start");
332 0 : if(selectionStart == selectionEnd)
333 0 : continue;
334 :
335 0 : result= selCon->GetSelection(TextRangeToSelection(textRangeType),
336 0 : getter_AddRefs(imeSel));
337 0 : NS_ASSERTION(NS_SUCCEEDED(result), "Cannot get selction");
338 0 : if(NS_FAILED(result))
339 0 : break;
340 :
341 0 : nsRefPtr<nsRange> newRange = new nsRange();
342 0 : result = newRange->SetStart(mElement,mOffset+selectionStart);
343 0 : NS_ASSERTION(NS_SUCCEEDED(result), "Cannot SetStart");
344 0 : if(NS_FAILED(result))
345 : break;
346 :
347 0 : result = newRange->SetEnd(mElement,mOffset+selectionEnd);
348 0 : NS_ASSERTION(NS_SUCCEEDED(result), "Cannot SetEnd");
349 0 : if(NS_FAILED(result))
350 : break;
351 :
352 0 : result = imeSel->AddRange(newRange);
353 0 : NS_ASSERTION(NS_SUCCEEDED(result), "Cannot AddRange");
354 0 : if(NS_FAILED(result))
355 : break;
356 :
357 : nsCOMPtr<nsISelectionPrivate> imeSelPriv(
358 0 : do_QueryInterface(imeSel));
359 0 : if (imeSelPriv) {
360 0 : nsTextRangeStyle textRangeStyle;
361 0 : result = textRange->GetRangeStyle(&textRangeStyle);
362 0 : NS_ASSERTION(NS_SUCCEEDED(result),
363 : "nsIPrivateTextRange::GetRangeStyle failed");
364 0 : if (NS_FAILED(result))
365 : break;
366 0 : result = imeSelPriv->SetTextRangeStyle(newRange, textRangeStyle);
367 0 : NS_ASSERTION(NS_SUCCEEDED(result),
368 : "nsISelectionPrivate::SetTextRangeStyle failed");
369 0 : if (NS_FAILED(result))
370 : break;
371 : } else {
372 0 : NS_WARNING("IME selection doesn't have nsISelectionPrivate");
373 : }
374 : } // if GetRangeEnd
375 : } // for textRangeListLength
376 0 : if(! setCaret) {
377 : // set cursor
378 0 : result = selection->Collapse(mElement,mOffset+mStringToInsert.Length());
379 0 : NS_ASSERTION(NS_SUCCEEDED(result), "Cannot Collapse");
380 : }
381 0 : result = selPriv->EndBatchChanges();
382 0 : NS_ASSERTION(NS_SUCCEEDED(result), "Cannot EndBatchChanges");
383 : } // if StartBatchChanges
384 : } // if GetSelection
385 :
386 0 : return result;
387 4392 : }
388 :
|