Back to index

lightning-sunbird  0.9+nobinonly
nsAccessibleText.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: NPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public
00006  * License Version 1.1 (the "License"); you may not use this file
00007  * except in compliance with the License. You may obtain a copy of
00008  * the License at http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS
00011  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
00012  * implied. See the License for the specific language governing
00013  * rights and limitations under the License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is Sun Microsystems, Inc.
00018  * Portions created by Sun Microsystems are Copyright (C) 2002 Sun
00019  * Microsystems, Inc. All Rights Reserved.
00020  *
00021  * Original Author: Kyle Yuan (kyle.yuan@sun.com)
00022  *
00023  * Contributor(s):
00024  *
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the NPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the NPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 // NOTE: alphabetically ordered
00041 #include "nsAccessibleText.h"
00042 #include "nsContentCID.h"
00043 #include "nsIClipboard.h"
00044 #include "nsIDOMAbstractView.h"
00045 #include "nsIDOMCharacterData.h"
00046 #include "nsIDOMDocument.h"
00047 #include "nsIDOMDocumentView.h"
00048 #include "nsIDOMHTMLBRElement.h"
00049 #include "nsIDOMHTMLDivElement.h"
00050 #include "nsIDOMHTMLInputElement.h"
00051 #include "nsIDOMHTMLTextAreaElement.h"
00052 #include "nsIDOMRange.h"
00053 #include "nsIDOMText.h"
00054 #include "nsIDOMWindow.h"
00055 #include "nsIDOMWindowInternal.h"
00056 #include "nsIDeviceContext.h"
00057 #include "nsIDocument.h"
00058 #include "nsIDocumentEncoder.h"
00059 #include "nsIFontMetrics.h"
00060 #include "nsIFrame.h"
00061 #include "nsIPlaintextEditor.h"
00062 #include "nsIRenderingContext.h"
00063 #include "nsITextContent.h"
00064 #include "nsIWidget.h"
00065 #include "nsStyleStruct.h"
00066 #include "nsTextFragment.h"
00067 #include "nsArray.h"
00068 
00069 static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
00070 
00071 // --------------------------------------------------------
00072 // nsAccessibleText Accessible
00073 // --------------------------------------------------------
00074 PRBool nsAccessibleText::gSuppressedNotifySelectionChanged = PR_FALSE;
00075 
00076 NS_IMPL_ISUPPORTS1(nsAccessibleText, nsIAccessibleText)
00077 
00078 
00082 nsAccessibleText::nsAccessibleText(nsIDOMNode *aNode)
00083 {
00084   mTextNode = aNode;
00085 }
00086 
00090 nsresult nsAccessibleText::GetSelections(nsISelectionController **aSelCon, nsISelection **aDomSel)
00091 {
00092   nsCOMPtr<nsIDOMDocument> domDoc;
00093   mTextNode->GetOwnerDocument(getter_AddRefs(domDoc));
00094   nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
00095   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
00096 
00097   nsIPresShell *shell = doc->GetShellAt(0);
00098   NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
00099 
00100   nsCOMPtr<nsIContent> content(do_QueryInterface(mTextNode));
00101   nsIFrame *frame = nsnull;
00102   shell->GetPrimaryFrameFor(content, &frame);
00103   NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
00104 
00105   // Get the selection and selection controller
00106   nsCOMPtr<nsISelectionController> selCon;
00107   nsCOMPtr<nsISelection> domSel;
00108   frame->GetSelectionController(shell->GetPresContext(),
00109                                 getter_AddRefs(selCon));
00110   if (selCon)
00111     selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(domSel));
00112 
00113   NS_ENSURE_TRUE(selCon && domSel, NS_ERROR_FAILURE);
00114 
00115   PRBool isSelectionCollapsed;
00116   domSel->GetIsCollapsed(&isSelectionCollapsed);
00117   // Don't perform any actions when the selection is not collapsed
00118   NS_ENSURE_TRUE(isSelectionCollapsed, NS_ERROR_FAILURE);
00119 
00120   if (aSelCon) {
00121     *aSelCon = selCon;
00122     NS_ADDREF(*aSelCon);
00123   }
00124 
00125   if (aDomSel) {
00126     *aDomSel = domSel;
00127     NS_ADDREF(*aDomSel);
00128   }
00129 
00130   return NS_OK;
00131 }
00132 
00133 nsresult nsAccessibleText::DOMPointToOffset(nsISupports *aClosure, nsIDOMNode* aNode, PRInt32 aNodeOffset, PRInt32* aResult)
00134 {
00135   NS_ENSURE_ARG_POINTER(aNode && aResult);
00136 
00137   *aResult = aNodeOffset;
00138 
00139   nsCOMPtr<nsIArray> domNodeArray(do_QueryInterface(aClosure));
00140   if (domNodeArray) {
00141     // Static text, calculate the offset from a given set of (text) node
00142     PRUint32 textLength, totalLength = 0;
00143     PRUint32 index, count;
00144     domNodeArray->GetLength(&count);
00145     for (index = 0; index < count; index++) {
00146       nsCOMPtr<nsIDOMNode> domNode(do_QueryElementAt(domNodeArray, index));
00147       if (aNode == domNode) {
00148         *aResult = aNodeOffset + totalLength;
00149         break;
00150       }
00151       nsCOMPtr<nsIDOMText> domText(do_QueryInterface(domNode));
00152       if (domText) {
00153         domText->GetLength(&textLength);
00154         totalLength += textLength;
00155       }
00156     }
00157 
00158     return NS_OK;
00159   }
00160 
00161   nsCOMPtr<nsIEditor> editor(do_QueryInterface(aClosure));
00162   if (editor) { // revised according to nsTextControlFrame::DOMPointToOffset
00163     // Editable text, calculate the offset from the editor
00164     nsCOMPtr<nsIDOMElement> rootElement;
00165     editor->GetRootElement(getter_AddRefs(rootElement));
00166     nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(rootElement));
00167 
00168     NS_ENSURE_TRUE(rootNode, NS_ERROR_FAILURE);
00169 
00170     nsCOMPtr<nsIDOMNodeList> nodeList;
00171 
00172     nsresult rv = rootNode->GetChildNodes(getter_AddRefs(nodeList));
00173     NS_ENSURE_SUCCESS(rv, rv);
00174     NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE);
00175 
00176     PRUint32 length = 0;
00177     rv = nodeList->GetLength(&length);
00178     NS_ENSURE_SUCCESS(rv, rv);
00179 
00180     if (!length || aNodeOffset < 0)
00181       return NS_OK;
00182 
00183     PRInt32 i, textOffset = 0;
00184     PRInt32 lastIndex = (PRInt32)length - 1;
00185 
00186     for (i = 0; i < (PRInt32)length; i++) {
00187       if (rootNode == aNode && i == aNodeOffset) {
00188         *aResult = textOffset;
00189         return NS_OK;
00190       }
00191 
00192       nsCOMPtr<nsIDOMNode> item;
00193       rv = nodeList->Item(i, getter_AddRefs(item));
00194       NS_ENSURE_SUCCESS(rv, rv);
00195       NS_ENSURE_TRUE(item, NS_ERROR_FAILURE);
00196 
00197       if (item == aNode) {
00198         *aResult = textOffset + aNodeOffset;
00199         return NS_OK;
00200       }
00201 
00202       nsCOMPtr<nsIDOMText> domText(do_QueryInterface(item));
00203 
00204       if (domText) {
00205         PRUint32 textLength = 0;
00206 
00207         rv = domText->GetLength(&textLength);
00208         NS_ENSURE_SUCCESS(rv, rv);
00209 
00210         textOffset += textLength;
00211       }
00212       else {
00213         // Must be a BR node. If it's not the last BR node
00214         // under the root, count it as a newline.
00215         if (i != lastIndex)
00216           ++textOffset;
00217       }
00218     }
00219 
00220     NS_ASSERTION((aNode == rootNode && aNodeOffset == (PRInt32)length),
00221                  "Invalid node offset!");
00222 
00223     *aResult = textOffset;
00224   }
00225 
00226   return NS_OK;
00227 }
00228 
00229 nsresult nsAccessibleText::OffsetToDOMPoint(nsISupports *aClosure, PRInt32 aOffset, nsIDOMNode** aResult, PRInt32* aPosition)
00230 {
00231   NS_ENSURE_ARG_POINTER(aResult && aPosition);
00232 
00233   *aResult = nsnull;
00234   *aPosition = 0;
00235 
00236   nsCOMPtr<nsIEditor> editor(do_QueryInterface(aClosure));
00237   if (editor) { // revised according to nsTextControlFrame::OffsetToDOMPoint
00238     nsCOMPtr<nsIDOMElement> rootElement;
00239     editor->GetRootElement(getter_AddRefs(rootElement));
00240     nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(rootElement));
00241 
00242     NS_ENSURE_TRUE(rootNode, NS_ERROR_FAILURE);
00243 
00244     nsCOMPtr<nsIDOMNodeList> nodeList;
00245 
00246     nsresult rv = rootNode->GetChildNodes(getter_AddRefs(nodeList));
00247     NS_ENSURE_SUCCESS(rv, rv);
00248     NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE);
00249 
00250     PRUint32 length = 0;
00251 
00252     rv = nodeList->GetLength(&length);
00253     NS_ENSURE_SUCCESS(rv, rv);
00254 
00255     if (!length || aOffset < 0) {
00256       *aPosition = 0;
00257       *aResult = rootNode;
00258       NS_ADDREF(*aResult);
00259       return NS_OK;
00260     }
00261 
00262     PRInt32 textOffset = 0;
00263     PRUint32 lastIndex = length - 1;
00264 
00265     for (PRUint32 i=0; i<length; i++) {
00266       nsCOMPtr<nsIDOMNode> item;
00267       rv = nodeList->Item(i, getter_AddRefs(item));
00268       NS_ENSURE_SUCCESS(rv, rv);
00269       NS_ENSURE_TRUE(item, NS_ERROR_FAILURE);
00270 
00271       nsCOMPtr<nsIDOMText> domText(do_QueryInterface(item));
00272 
00273       if (domText) {
00274         PRUint32 textLength = 0;
00275 
00276         rv = domText->GetLength(&textLength);
00277         NS_ENSURE_SUCCESS(rv, rv);
00278 
00279         // Check if aOffset falls within this range.
00280         if (aOffset >= textOffset && aOffset <= textOffset+(PRInt32)textLength) {
00281           *aPosition = aOffset - textOffset;
00282           *aResult = item;
00283           NS_ADDREF(*aResult);
00284           return NS_OK;
00285         }
00286 
00287         textOffset += textLength;
00288 
00289         // If there aren't any more siblings after this text node,
00290         // return the point at the end of this text node!
00291 
00292         if (i == lastIndex) {
00293           *aPosition = textLength;
00294           *aResult = item;
00295           NS_ADDREF(*aResult);
00296           return NS_OK;
00297         }
00298       }
00299       else {
00300         // Must be a BR node, count it as a newline.
00301 
00302         if (aOffset == textOffset || i == lastIndex) {
00303           // We've found the correct position, or aOffset takes us
00304           // beyond the last child under rootNode, just return the point
00305           // under rootNode that is in front of this br.
00306 
00307           *aPosition = i;
00308           *aResult = rootNode;
00309           NS_ADDREF(*aResult);
00310           return NS_OK;
00311         }
00312 
00313         ++textOffset;
00314       }
00315     }
00316   }
00317 
00318   return NS_ERROR_FAILURE;
00319 }
00320 
00321 nsresult nsAccessibleText::GetCurrentOffset(nsISupports *aClosure, nsISelection *aDomSel, PRInt32 *aOffset)
00322 {
00323   nsCOMPtr<nsIDOMNode> focusNode;
00324   aDomSel->GetFocusNode(getter_AddRefs(focusNode));
00325   aDomSel->GetFocusOffset(aOffset);
00326   return DOMPointToOffset(aClosure, focusNode, *aOffset, aOffset);
00327 }
00328 
00329 /*
00330 Gets the specified text.
00331 
00332 aBoundaryType means:
00333 
00334 ATK_TEXT_BOUNDARY_CHAR
00335   The character before/at/after the offset is returned.
00336 
00337 ATK_TEXT_BOUNDARY_WORD_START
00338   The returned string is from the word start before/at/after the offset to the next word start.
00339 
00340 ATK_TEXT_BOUNDARY_WORD_END
00341   The returned string is from the word end before/at/after the offset to the next work end.
00342 
00343 ATK_TEXT_BOUNDARY_SENTENCE_START
00344   The returned string is from the sentence start before/at/after the offset to the next sentence start.
00345 
00346 ATK_TEXT_BOUNDARY_SENTENCE_END
00347   The returned string is from the sentence end before/at/after the offset to the next sentence end.
00348 
00349 ATK_TEXT_BOUNDARY_LINE_START
00350   The returned string is from the line start before/at/after the offset to the next line start.
00351 
00352 ATK_TEXT_BOUNDARY_LINE_END
00353   The returned string is from the line end before/at/after the offset to the next line start.
00354 */
00355 nsresult nsAccessibleText::GetTextHelperCore(EGetTextType aType, nsAccessibleTextBoundary aBoundaryType,
00356                                          PRInt32 aOffset, PRInt32 *aStartOffset, PRInt32 *aEndOffset,
00357                                          nsISelectionController *aSelCon, nsISelection *aDomSel,
00358                                          nsISupports *aClosure, nsAString &aText)
00359 {
00360   PRInt32 rangeCount;
00361   nsCOMPtr<nsIDOMRange> range, oldRange;
00362   aDomSel->GetRangeCount(&rangeCount);
00363 
00364   if (rangeCount == 0) { // ever happen?
00365     SetCaretOffset(aOffset); // a new range will be added here
00366     rangeCount++;
00367   }
00368   aDomSel->GetRangeAt(rangeCount - 1, getter_AddRefs(range));
00369   NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
00370 
00371   // backup the original selection range to restore the selection status later
00372   range->CloneRange(getter_AddRefs(oldRange));
00373 
00374   // Step1: move caret to an appropriate start position
00375   // Step2: move caret to end postion and select the text
00376   PRBool isStep1Forward, isStep2Forward;  // Moving directions for two steps
00377   switch (aType)
00378   {
00379   case eGetBefore:
00380     isStep1Forward = PR_FALSE;
00381     isStep2Forward = PR_FALSE;
00382     break;
00383   case eGetAt:
00384     isStep1Forward = PR_FALSE;
00385     isStep2Forward = PR_TRUE;
00386     break;
00387   case eGetAfter:
00388     isStep1Forward = PR_TRUE;
00389     isStep2Forward = PR_TRUE;
00390     break;
00391   default:
00392     return NS_ERROR_INVALID_ARG;
00393   }
00394 
00395   // The start/end focus node may be not our mTextNode
00396   nsCOMPtr<nsIDOMNode> startFocusNode, endFocusNode;
00397   switch (aBoundaryType)
00398   {
00399   case BOUNDARY_CHAR:
00400     if (aType == eGetAfter) { // We need the character next to current position
00401       aSelCon->CharacterMove(isStep1Forward, PR_FALSE);
00402       GetCurrentOffset(aClosure, aDomSel, aStartOffset);
00403     }
00404     aSelCon->CharacterMove(isStep2Forward, PR_TRUE);
00405     GetCurrentOffset(aClosure, aDomSel, aEndOffset);
00406     break;
00407   case BOUNDARY_WORD_START:
00408     {
00409     PRBool dontMove = PR_FALSE;
00410     // If we are at the word boundary, don't move the caret in the first step
00411     if (aOffset == 0)
00412       dontMove = PR_TRUE;
00413     else {
00414       PRUnichar prevChar;
00415       GetCharacterAtOffset(aOffset - 1, &prevChar);
00416       if (prevChar == ' ' || prevChar == '\t' || prevChar == '\n')
00417         dontMove = PR_TRUE;
00418     }
00419     if (!dontMove) {
00420       aSelCon->WordMove(isStep1Forward, PR_FALSE); // Move caret to previous/next word start boundary
00421       GetCurrentOffset(aClosure, aDomSel, aStartOffset);
00422     }
00423     aSelCon->WordMove(isStep2Forward, PR_TRUE);  // Select previous/next word
00424     GetCurrentOffset(aClosure, aDomSel, aEndOffset);
00425     }
00426     break;
00427   case BOUNDARY_LINE_START:
00428     if (aType != eGetAt) {
00429       // XXX, don't support yet
00430       return NS_ERROR_NOT_IMPLEMENTED;
00431     }
00432     aSelCon->IntraLineMove(PR_FALSE, PR_FALSE);  // Move caret to the line start
00433     GetCurrentOffset(aClosure, aDomSel, aStartOffset);
00434     aSelCon->IntraLineMove(PR_TRUE, PR_TRUE);    // Move caret to the line end and select the whole line
00435     GetCurrentOffset(aClosure, aDomSel, aEndOffset);
00436     break;
00437   case BOUNDARY_WORD_END:
00438     {
00439     // please refer to atk implementation (atktext.c)
00440     // for specification of ATK_TEXT_BOUNDARY_WORD_END when before/at/after offset
00441     // XXX, need to follow exact definition of ATK_TEXT_BOUNDARY_WORD_END
00442 
00443     if (aType != eGetAt) {
00444       // XXX, don't support yet
00445       return NS_ERROR_NOT_IMPLEMENTED;
00446     }
00447 
00448     // Example of current code: _AB_CD_E_ ("_" is space)
00449     // offset      return string   startOffset endOffset
00450     //      0      AB_             1           4
00451     //      1      AB_             1           4
00452     //      2      AB_             1           4
00453     //      3      AB_             1           4
00454     //      4      CD_             4           7
00455     //      5      CD_             4           7
00456     //      6      CD_             4           7
00457     //      7      E_              7           9
00458     //      8      E_              7           9
00459 
00460     PRUnichar offsetChar;
00461     nsresult rv = GetCharacterAtOffset(aOffset, &offsetChar);
00462     NS_ENSURE_SUCCESS(rv, rv);
00463     PRBool isOffsetEmpty =  offsetChar == ' ' || offsetChar == '\t' || offsetChar == '\n';
00464 
00465     PRInt32 stepBackwardCount = 0; // Times of move backward to find the word(e.g. "AB_") start boundary
00466     if (aOffset == 0) {
00467       if (isOffsetEmpty)
00468         aSelCon->WordMove(PR_TRUE, PR_FALSE); // Move caret to the first word start boundary
00469     }
00470     else {
00471       PRUnichar prevChar;
00472       GetCharacterAtOffset(aOffset - 1, &prevChar);
00473       PRBool isPrevEmpty =  prevChar == ' ' || prevChar == '\t' || prevChar == '\n';
00474       if (!isPrevEmpty)
00475         stepBackwardCount = 1;
00476       else if (isOffsetEmpty)
00477         stepBackwardCount = 2;
00478       else
00479         stepBackwardCount = 0;
00480 
00481       PRInt32 step;
00482       for (step = 0; step < stepBackwardCount; step++)
00483         aSelCon->WordMove(PR_FALSE, PR_FALSE); // Move caret to current word start boundary
00484     }
00485 
00486     GetCurrentOffset(aClosure, aDomSel, aStartOffset);
00487     // Move twice to select a "word"
00488     aSelCon->WordMove(PR_TRUE, PR_TRUE);
00489     aSelCon->WordMove(PR_TRUE, PR_TRUE);
00490     GetCurrentOffset(aClosure, aDomSel, aEndOffset);
00491     }
00492     break;
00493   case BOUNDARY_LINE_END:
00494   case BOUNDARY_SENTENCE_START:
00495   case BOUNDARY_SENTENCE_END:
00496   case BOUNDARY_ATTRIBUTE_RANGE:
00497     return NS_ERROR_NOT_IMPLEMENTED;
00498   default:
00499     return NS_ERROR_INVALID_ARG;
00500   }
00501 
00502   nsXPIDLString text;
00503   // Get text from selection
00504   nsresult rv = aDomSel->ToString(getter_Copies(text));
00505   aDomSel->RemoveAllRanges();
00506   // restore the original selection range
00507   aDomSel->AddRange(oldRange);
00508   NS_ENSURE_SUCCESS(rv, rv);
00509 
00510   aText = text;
00511 
00512   // Ensure aStartOffset <= aEndOffset
00513   if (*aStartOffset > *aEndOffset) {
00514     PRInt32 tmp = *aStartOffset;
00515     *aStartOffset = *aEndOffset;
00516     *aEndOffset = tmp;
00517   }
00518 
00519   return NS_OK;
00520 }
00521 
00522 nsresult nsAccessibleText::GetTextHelper(EGetTextType aType, nsAccessibleTextBoundary aBoundaryType,
00523                                          PRInt32 aOffset, PRInt32 *aStartOffset, PRInt32 *aEndOffset,
00524                                          nsISupports *aClosure, nsAString &aText)
00525 {
00526   NS_ENSURE_TRUE((aOffset >= 0), NS_ERROR_INVALID_ARG);
00527   
00528   nsCOMPtr<nsISelectionController> selCon;
00529   nsCOMPtr<nsISelection> domSel;
00530 
00531   nsresult rv = GetSelections(getter_AddRefs(selCon), getter_AddRefs(domSel));
00532   NS_ENSURE_SUCCESS(rv, rv);
00533 
00534   //backup old settings
00535   PRInt16 displaySelection;
00536   selCon->GetDisplaySelection(&displaySelection);
00537   PRBool caretEnable;
00538   selCon->GetCaretEnabled(&caretEnable);
00539 
00540   //turn off display and caret
00541   selCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
00542   selCon->SetCaretEnabled(PR_FALSE);
00543 
00544   //turn off nsCaretAccessible::NotifySelectionChanged
00545   gSuppressedNotifySelectionChanged = PR_TRUE;
00546 
00547   PRInt32 caretOffset = -1;
00548   if (NS_SUCCEEDED(GetCaretOffset(&caretOffset))) {
00549     if (caretOffset != aOffset)
00550       SetCaretOffset(aOffset);
00551   }
00552 
00553   *aStartOffset = *aEndOffset = aOffset;
00554   rv = GetTextHelperCore(aType, aBoundaryType, aOffset, aStartOffset, aEndOffset, selCon, domSel, aClosure, aText);
00555 
00556   //restore caret offset
00557   if (caretOffset >= 0) {
00558     SetCaretOffset(caretOffset);
00559   }
00560 
00561   //turn on nsCaretAccessible::NotifySelectionChanged
00562   gSuppressedNotifySelectionChanged = PR_FALSE;
00563 
00564   //restore old settings
00565   selCon->SetDisplaySelection(displaySelection);
00566   selCon->SetCaretEnabled(caretEnable);
00567 
00568   return rv;
00569 }
00570 
00574 /*
00575  * Gets the offset position of the caret (cursor).
00576  */
00577 NS_IMETHODIMP nsAccessibleText::GetCaretOffset(PRInt32 *aCaretOffset)
00578 {
00579   nsCOMPtr<nsISelection> domSel;
00580   nsresult rv = GetSelections(nsnull, getter_AddRefs(domSel));
00581   NS_ENSURE_SUCCESS(rv, rv);
00582 
00583   nsCOMPtr<nsIDOMNode> focusNode;
00584   domSel->GetFocusNode(getter_AddRefs(focusNode));
00585   if (focusNode != mTextNode)
00586     return NS_ERROR_FAILURE;
00587 
00588   return domSel->GetFocusOffset(aCaretOffset);
00589 }
00590 
00591 /*
00592  * Sets the caret (cursor) position to the specified offset.
00593  */
00594 NS_IMETHODIMP nsAccessibleText::SetCaretOffset(PRInt32 aCaretOffset)
00595 {
00596   nsCOMPtr<nsISelection> domSel;
00597   nsresult rv = GetSelections(nsnull, getter_AddRefs(domSel));
00598   NS_ENSURE_SUCCESS(rv, rv);
00599 
00600   nsCOMPtr<nsIDOMRange> range(do_CreateInstance(kRangeCID));
00601   NS_ENSURE_TRUE(range, NS_ERROR_OUT_OF_MEMORY);
00602 
00603   range->SetStart(mTextNode, aCaretOffset);
00604   range->SetEnd(mTextNode, aCaretOffset);
00605   domSel->RemoveAllRanges();
00606   return domSel->AddRange(range);
00607 }
00608 
00609 /*
00610  * Gets the character count.
00611  */
00612 NS_IMETHODIMP nsAccessibleText::GetCharacterCount(PRInt32 *aCharacterCount)
00613 {
00614   nsCOMPtr<nsITextContent> textContent(do_QueryInterface(mTextNode));
00615   if (!textContent)
00616     return NS_ERROR_FAILURE;
00617 
00618   *aCharacterCount = textContent->TextLength();
00619 
00620   return NS_OK;
00621 }
00622 
00623 /*
00624  * Gets the number of selected regions.
00625  */
00626 NS_IMETHODIMP nsAccessibleText::GetSelectionCount(PRInt32 *aSelectionCount)
00627 {
00628   nsCOMPtr<nsISelection> domSel;
00629   nsresult rv = GetSelections(nsnull, getter_AddRefs(domSel));
00630   NS_ENSURE_SUCCESS(rv, rv);
00631 
00632   PRBool isSelectionCollapsed;
00633   rv = domSel->GetIsCollapsed(&isSelectionCollapsed);
00634   NS_ENSURE_SUCCESS(rv, rv);
00635 
00636   if (isSelectionCollapsed)
00637     *aSelectionCount = 0;
00638 
00639   rv = domSel->GetRangeCount(aSelectionCount);
00640   NS_ENSURE_SUCCESS(rv, rv);
00641 
00642   return NS_OK;
00643 }
00644 
00645 /*
00646  * Gets the specified text.
00647  */
00648 NS_IMETHODIMP nsAccessibleText::GetText(PRInt32 aStartOffset, PRInt32 aEndOffset, nsAString &aText)
00649 {
00650   nsAutoString text;
00651   mTextNode->GetNodeValue(text);
00652   if (aEndOffset == -1) // get all text from aStartOffset
00653     aEndOffset = text.Length();
00654   aText = Substring(text, aStartOffset, aEndOffset - aStartOffset);
00655   return NS_OK;
00656 }
00657 
00658 NS_IMETHODIMP nsAccessibleText::GetTextBeforeOffset(PRInt32 aOffset, nsAccessibleTextBoundary aBoundaryType,
00659                                                     PRInt32 *aStartOffset, PRInt32 *aEndOffset, nsAString & aText)
00660 {
00661   return GetTextHelper(eGetBefore, aBoundaryType, aOffset, aStartOffset, aEndOffset, nsnull, aText);
00662 }
00663 
00664 NS_IMETHODIMP nsAccessibleText::GetTextAtOffset(PRInt32 aOffset, nsAccessibleTextBoundary aBoundaryType,
00665                                                 PRInt32 *aStartOffset, PRInt32 *aEndOffset, nsAString & aText)
00666 {
00667   return GetTextHelper(eGetAt, aBoundaryType, aOffset, aStartOffset, aEndOffset, nsnull, aText);
00668 }
00669 
00670 NS_IMETHODIMP nsAccessibleText::GetTextAfterOffset(PRInt32 aOffset, nsAccessibleTextBoundary aBoundaryType,
00671                                                    PRInt32 *aStartOffset, PRInt32 *aEndOffset, nsAString & aText)
00672 {
00673   return GetTextHelper(eGetAfter, aBoundaryType, aOffset, aStartOffset, aEndOffset, nsnull, aText);
00674 }
00675 
00676 /*
00677  * Gets the specified text.
00678  */
00679 NS_IMETHODIMP nsAccessibleText::GetCharacterAtOffset(PRInt32 aOffset, PRUnichar *aCharacter)
00680 {
00681   nsAutoString text;
00682   nsresult rv = GetText(aOffset, aOffset + 1, text);
00683   NS_ENSURE_SUCCESS(rv, rv);
00684   *aCharacter = text.First();
00685   return NS_OK;
00686 }
00687 
00688 NS_IMETHODIMP nsAccessibleText::GetAttributeRange(PRInt32 aOffset, PRInt32 *aRangeStartOffset, 
00689                                                   PRInt32 *aRangeEndOffset, nsISupports **aAttribute)
00690 {
00691   // will do better job later
00692   *aRangeStartOffset = aOffset;
00693   GetCharacterCount(aRangeEndOffset);
00694   *aAttribute = nsnull;
00695   return NS_OK;
00696 }
00697 
00698 /*
00699  * Given an offset, the x, y, width, and height values are filled appropriately.
00700  */
00701 NS_IMETHODIMP nsAccessibleText::GetCharacterExtents(PRInt32 aOffset,
00702               PRInt32 *aX, PRInt32 *aY, PRInt32 *aWidth, PRInt32 *aHeight,
00703               nsAccessibleCoordType aCoordType)
00704 {
00705   nsCOMPtr<nsIDOMDocument> domDoc;
00706   mTextNode->GetOwnerDocument(getter_AddRefs(domDoc));
00707   nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
00708   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
00709 
00710   nsIPresShell *shell = doc->GetShellAt(0);
00711   NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
00712 
00713   nsPresContext *context = shell->GetPresContext();
00714   NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
00715 
00716   nsCOMPtr<nsIContent> content(do_QueryInterface(mTextNode));
00717   NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
00718 
00719   nsIFrame *frame = nsnull;
00720   shell->GetPrimaryFrameFor(content, &frame);
00721   NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
00722 
00723   nsIntRect frameScreenRect = frame->GetScreenRectExternal();
00724 
00725   nsCOMPtr<nsIRenderingContext> rc;
00726   shell->CreateRenderingContext(frame, getter_AddRefs(rc));
00727   NS_ENSURE_TRUE(rc, NS_ERROR_FAILURE);
00728 
00729   const nsStyleFont *font = frame->GetStyleFont();
00730 
00731   const nsStyleVisibility *visibility = frame->GetStyleVisibility();
00732 
00733   if (NS_FAILED(rc->SetFont(font->mFont, visibility->mLangGroup))) {
00734     return NS_ERROR_FAILURE;
00735   }
00736 
00737   nsIFontMetrics *fm;
00738   rc->GetFontMetrics(fm);
00739   NS_ENSURE_TRUE(fm, NS_ERROR_FAILURE);
00740 
00741   float t2p = context->TwipsToPixels();
00742 
00743   PRUnichar ch;
00744   if (NS_SUCCEEDED(GetCharacterAtOffset(aOffset, &ch))) {
00745     //Getting width
00746     nscoord tmpWidth;
00747     if (NS_SUCCEEDED(rc->GetWidth(ch, tmpWidth))) {
00748       *aWidth = NSTwipsToIntPixels(tmpWidth, t2p);
00749     }
00750 
00751     //Getting height
00752     nscoord tmpHeight;
00753     if (NS_SUCCEEDED(fm->GetHeight(tmpHeight))) {
00754       *aHeight = NSTwipsToIntPixels(tmpHeight, t2p);
00755     }
00756   }
00757   else {
00758     //GetCharacterAtOffset() will fail when there is no text
00759     *aWidth = *aHeight = 0;
00760   }
00761 
00762   //add the width of the string before current char
00763   nsAutoString beforeString;
00764   nscoord beforeWidth;
00765   if (NS_SUCCEEDED(GetText(0, aOffset, beforeString)) &&
00766       NS_SUCCEEDED(rc->GetWidth(beforeString, beforeWidth))) {
00767     frameScreenRect.x += NSTwipsToIntPixels(beforeWidth, t2p);
00768   }
00769 
00770   PRInt32 screenX = 0, screenY = 0;
00771   if (aCoordType == COORD_TYPE_WINDOW) {
00772     //co-ord type = window
00773     nsCOMPtr<nsIDOMDocumentView> docView(do_QueryInterface(doc));
00774     NS_ENSURE_TRUE(docView, NS_ERROR_FAILURE);
00775 
00776     nsCOMPtr<nsIDOMAbstractView> abstractView;
00777     docView->GetDefaultView(getter_AddRefs(abstractView));
00778     NS_ENSURE_TRUE(abstractView, NS_ERROR_FAILURE);
00779 
00780     nsCOMPtr<nsIDOMWindowInternal> windowInter(do_QueryInterface(abstractView));
00781     NS_ENSURE_TRUE(windowInter, NS_ERROR_FAILURE);
00782 
00783     if (NS_FAILED(windowInter->GetScreenX(&screenX)) ||
00784         NS_FAILED(windowInter->GetScreenY(&screenY))) {
00785       return NS_ERROR_FAILURE;
00786     }
00787   }
00788   // else default: co-ord type = screen
00789 
00790   *aX = frameScreenRect.x - screenX;
00791   *aY = frameScreenRect.y - screenY;
00792 
00793   return NS_OK;
00794 }
00795 
00796 /*
00797  * Gets the offset of the character located at coordinates x and y. x and y are interpreted as being relative to
00798  * the screen or this widget's window depending on coords.
00799  */
00800 NS_IMETHODIMP nsAccessibleText::GetOffsetAtPoint(PRInt32 aX, PRInt32 aY, nsAccessibleCoordType aCoordType, PRInt32 *aOffset)
00801 {
00802   // will do better job later
00803   *aOffset = 0;
00804   return NS_ERROR_NOT_IMPLEMENTED;
00805 }
00806 
00807 /*
00808  * Gets the start and end offset of the specified selection.
00809  */
00810 NS_IMETHODIMP nsAccessibleText::GetSelectionBounds(PRInt32 aSelectionNum, PRInt32 *aStartOffset, PRInt32 *aEndOffset)
00811 {
00812   nsCOMPtr<nsISelection> domSel;
00813   nsresult rv = GetSelections(nsnull, getter_AddRefs(domSel));
00814   NS_ENSURE_SUCCESS(rv, rv);
00815 
00816   PRInt32 rangeCount;
00817   domSel->GetRangeCount(&rangeCount);
00818   if (aSelectionNum < 0 || aSelectionNum >= rangeCount)
00819     return NS_ERROR_INVALID_ARG;
00820 
00821   nsCOMPtr<nsIDOMRange> range;
00822   domSel->GetRangeAt(aSelectionNum, getter_AddRefs(range));
00823   range->GetStartOffset(aStartOffset);
00824   range->GetEndOffset(aEndOffset);
00825   return NS_OK;
00826 }
00827 
00828 /*
00829  * Changes the start and end offset of the specified selection.
00830  */
00831 NS_IMETHODIMP nsAccessibleText::SetSelectionBounds(PRInt32 aSelectionNum, PRInt32 aStartOffset, PRInt32 aEndOffset)
00832 {
00833   nsCOMPtr<nsISelection> domSel;
00834   nsresult rv = GetSelections(nsnull, getter_AddRefs(domSel));
00835   NS_ENSURE_SUCCESS(rv, rv);
00836 
00837   PRInt32 rangeCount;
00838   domSel->GetRangeCount(&rangeCount);
00839   if (aSelectionNum < 0 || aSelectionNum >= rangeCount)
00840     return NS_ERROR_INVALID_ARG;
00841 
00842   nsCOMPtr<nsIDOMRange> range;
00843   domSel->GetRangeAt(aSelectionNum, getter_AddRefs(range));
00844 
00845   nsCOMPtr<nsIDOMNode> startParent;
00846   nsCOMPtr<nsIDOMNode> endParent;
00847   range->GetStartContainer(getter_AddRefs(startParent));
00848   range->GetEndContainer(getter_AddRefs(endParent));
00849   PRInt32 oldEndOffset;
00850   range->GetEndOffset(&oldEndOffset);
00851   // to avoid set start point after the current end point
00852   if (aStartOffset < oldEndOffset) {
00853     range->SetStart(startParent, aStartOffset);
00854     range->SetEnd(endParent, aEndOffset);
00855   }
00856   else {
00857     range->SetEnd(endParent, aEndOffset);
00858     range->SetStart(startParent, aStartOffset);
00859   }
00860   return NS_OK;
00861 }
00862 
00863 /*
00864  * Adds a selection bounded by the specified offsets.
00865  */
00866 NS_IMETHODIMP nsAccessibleText::AddSelection(PRInt32 aStartOffset, PRInt32 aEndOffset)
00867 {
00868   nsCOMPtr<nsISelectionController> selCon;
00869   nsCOMPtr<nsISelection> domSel;
00870 
00871   if (NS_FAILED(GetSelections(getter_AddRefs(selCon), getter_AddRefs(domSel))))
00872     return NS_ERROR_FAILURE;
00873 
00874   nsCOMPtr<nsIDOMRange> range(do_CreateInstance(kRangeCID));
00875   if (! range)
00876     return NS_ERROR_OUT_OF_MEMORY;
00877 
00878   range->SetStart(mTextNode, aStartOffset);
00879   range->SetEnd(mTextNode, aEndOffset);
00880   return domSel->AddRange(range);
00881 }
00882 
00883 /*
00884  * Removes the specified selection.
00885  */
00886 NS_IMETHODIMP nsAccessibleText::RemoveSelection(PRInt32 aSelectionNum)
00887 {
00888   nsCOMPtr<nsISelection> domSel;
00889   nsresult rv = GetSelections(nsnull, getter_AddRefs(domSel));
00890   NS_ENSURE_SUCCESS(rv, rv);
00891 
00892   PRInt32 rangeCount;
00893   domSel->GetRangeCount(&rangeCount);
00894   if (aSelectionNum < 0 || aSelectionNum >= rangeCount)
00895     return NS_ERROR_INVALID_ARG;
00896 
00897   nsCOMPtr<nsIDOMRange> range;
00898   domSel->GetRangeAt(aSelectionNum, getter_AddRefs(range));
00899   return domSel->RemoveRange(range);
00900 }
00901 
00902 // --------------------------------------------------------
00903 // nsAccessibleEditableText Accessible
00904 // --------------------------------------------------------
00909 NS_IMPL_ISUPPORTS3(nsAccessibleEditableText, nsIAccessibleText, nsIAccessibleEditableText, nsIEditActionListener)
00910 
00911 nsAccessibleEditableText::nsAccessibleEditableText(nsIDOMNode *aNode):
00912 nsAccessibleText(aNode)
00913 {
00914 }
00915 
00916 void nsAccessibleEditableText::ShutdownEditor()
00917 {
00918   if (mPlainEditor) {
00919     mPlainEditor->RemoveEditActionListener(this);
00920     mPlainEditor = nsnull;
00921   }
00922 }
00923 
00928 void nsAccessibleEditableText::SetEditor(nsIEditor* aEditor)
00929 {
00930   mPlainEditor = aEditor;
00931   if (mPlainEditor)
00932     mPlainEditor->AddEditActionListener(this);
00933 }
00934 
00935 PRBool nsAccessibleEditableText::IsSingleLineTextControl(nsIDOMNode *aDomNode)
00936 {
00937   nsCOMPtr<nsIDOMHTMLInputElement> input(do_QueryInterface(aDomNode));
00938   return input ? PR_TRUE : PR_FALSE;
00939 }
00940 
00941 nsresult nsAccessibleEditableText::FireTextChangeEvent(AtkTextChange *aTextData)
00942 {
00943   nsCOMPtr<nsIAccessible> accessible(do_QueryInterface(NS_STATIC_CAST(nsIAccessibleText*, this)));
00944   nsCOMPtr<nsPIAccessible> privAccessible(do_QueryInterface(accessible));
00945   if (privAccessible) {
00946 #ifdef DEBUG
00947     printf("  [start=%d, length=%d, add=%d]\n", aTextData->start, aTextData->length, aTextData->add);
00948 #endif
00949     privAccessible->FireToolkitEvent(nsIAccessibleEvent::EVENT_ATK_TEXT_CHANGE, accessible, aTextData);
00950   }
00951 
00952   return NS_OK;
00953 }
00954 
00955 nsresult nsAccessibleEditableText::GetSelections(nsISelectionController **aSelCon, nsISelection **aDomSel)
00956 {
00957   nsCOMPtr<nsIDocument> doc(do_QueryInterface(mTextNode));
00958   if (!doc)
00959     return nsAccessibleText::GetSelections(aSelCon, aDomSel);
00960     
00961   // it's composer
00962   if (!mPlainEditor)
00963     return NS_ERROR_FAILURE;
00964 
00965   nsCOMPtr<nsISelectionController> selCon;
00966   nsCOMPtr<nsISelection> domSel;
00967   mPlainEditor->GetSelectionController(getter_AddRefs(selCon));
00968   if (selCon)
00969     selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(domSel));
00970 
00971   NS_ENSURE_TRUE(selCon && domSel, NS_ERROR_FAILURE);
00972 
00973   PRBool isSelectionCollapsed;
00974   domSel->GetIsCollapsed(&isSelectionCollapsed);
00975   // Don't perform any actions when the selection is not collapsed
00976   if (!isSelectionCollapsed) {
00977     return NS_ERROR_FAILURE;
00978   }
00979 
00980   if (aSelCon) {
00981     *aSelCon = selCon;
00982     NS_ADDREF(*aSelCon);
00983   }
00984 
00985   if (aDomSel) {
00986     *aDomSel = domSel;
00987     NS_ADDREF(*aDomSel);
00988   }
00989 
00990   return NS_OK;
00991 }
00992 
00993 nsITextControlFrame* nsAccessibleEditableText::GetTextFrame()
00994 {
00995   nsCOMPtr<nsIDOMDocument> domDoc;
00996   mTextNode->GetOwnerDocument(getter_AddRefs(domDoc));
00997   nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
00998   if (!doc) // that could be a composer
00999     return nsnull;
01000 
01001   nsIPresShell *shell = doc->GetShellAt(0);
01002   NS_ENSURE_TRUE(shell, nsnull);
01003 
01004   nsCOMPtr<nsIContent> content(do_QueryInterface(mTextNode));
01005   nsIFrame *frame = nsnull;
01006   shell->GetPrimaryFrameFor(content, &frame);
01007   NS_ENSURE_TRUE(frame, nsnull);
01008 
01009   nsITextControlFrame *textFrame;
01010   frame->QueryInterface(NS_GET_IID(nsITextControlFrame), (void**)&textFrame);
01011 
01012   return textFrame;
01013 }
01014 
01015 nsresult nsAccessibleEditableText::GetSelectionRange(PRInt32 *aStartPos, PRInt32 *aEndPos)
01016 {
01017   *aStartPos = 0;
01018   *aEndPos = 0;
01019 
01020   nsITextControlFrame *textFrame = GetTextFrame();
01021   if (textFrame) {
01022     return textFrame->GetSelectionRange(aStartPos, aEndPos);
01023   }
01024   else {
01025     // editor, revised according to nsTextControlFrame::GetSelectionRange
01026     NS_ENSURE_TRUE(mPlainEditor, NS_ERROR_FAILURE);
01027     nsCOMPtr<nsISelection> selection;
01028     nsresult rv = mPlainEditor->GetSelection(getter_AddRefs(selection));  
01029     NS_ENSURE_SUCCESS(rv, rv);
01030     NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
01031 
01032     PRInt32 numRanges = 0;
01033     selection->GetRangeCount(&numRanges);
01034     NS_ENSURE_TRUE(numRanges >= 1, NS_ERROR_FAILURE);
01035 
01036     nsCOMPtr<nsIDOMRange> firstRange;
01037     rv = selection->GetRangeAt(0, getter_AddRefs(firstRange));
01038     NS_ENSURE_TRUE(firstRange, NS_ERROR_FAILURE);
01039 
01040     nsCOMPtr<nsIDOMNode> startNode, endNode;
01041     PRInt32 startOffset = 0, endOffset = 0;
01042 
01043     // Get the start point of the range.
01044     rv = firstRange->GetStartContainer(getter_AddRefs(startNode));
01045     NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
01046 
01047     rv = firstRange->GetStartOffset(&startOffset);
01048     NS_ENSURE_SUCCESS(rv, rv);
01049 
01050     // Get the end point of the range.
01051     rv = firstRange->GetEndContainer(getter_AddRefs(endNode));
01052     NS_ENSURE_TRUE(endNode, NS_ERROR_FAILURE);
01053 
01054     rv = firstRange->GetEndOffset(&endOffset);
01055     NS_ENSURE_SUCCESS(rv, rv);
01056 
01057     // Convert the start/end point to a selection offset.
01058     rv = DOMPointToOffset(mPlainEditor, startNode, startOffset, aStartPos);
01059     NS_ENSURE_SUCCESS(rv, rv);
01060 
01061     rv = DOMPointToOffset(mPlainEditor, endNode, endOffset, aEndPos);
01062     NS_ENSURE_SUCCESS(rv, rv);
01063   }
01064 
01065   return NS_OK;
01066 }
01067 
01068 nsresult nsAccessibleEditableText::SetSelectionRange(PRInt32 aStartPos, PRInt32 aEndPos)
01069 {
01070   nsITextControlFrame *textFrame = GetTextFrame();
01071   if (textFrame) {
01072     return textFrame->SetSelectionRange(aStartPos, aEndPos);
01073   }
01074   else {
01075     // editor, revised according to nsTextControlFrame::SetSelectionRange
01076     NS_ENSURE_TRUE(mPlainEditor, NS_ERROR_FAILURE);
01077     if (aStartPos > aEndPos)
01078       return NS_ERROR_FAILURE;
01079 
01080     nsCOMPtr<nsIDOMNode> startNode, endNode;
01081     PRInt32 startOffset, endOffset;
01082 
01083     nsresult rv = OffsetToDOMPoint(mPlainEditor, aStartPos, getter_AddRefs(startNode), &startOffset);
01084     NS_ENSURE_SUCCESS(rv, rv);
01085 
01086     if (aStartPos == aEndPos) {
01087       endNode   = startNode;
01088       endOffset = startOffset;
01089     }
01090     else {
01091       rv = OffsetToDOMPoint(mPlainEditor, aEndPos, getter_AddRefs(endNode), &endOffset);
01092       NS_ENSURE_SUCCESS(rv, rv);
01093     }
01094 
01095     // Create a new range to represent the new selection.
01096     nsCOMPtr<nsIDOMRange> range = do_CreateInstance(kRangeCID);
01097     NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
01098 
01099     rv = range->SetStart(startNode, startOffset);
01100     NS_ENSURE_SUCCESS(rv, rv);
01101 
01102     rv = range->SetEnd(endNode, endOffset);
01103     NS_ENSURE_SUCCESS(rv, rv);
01104 
01105     // Get the selection, clear it and add the new range to it!
01106     nsCOMPtr<nsISelection> selection;
01107     mPlainEditor->GetSelection(getter_AddRefs(selection));
01108     NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
01109 
01110     rv = selection->RemoveAllRanges();
01111     NS_ENSURE_SUCCESS(rv, rv);
01112 
01113     return selection->AddRange(range);
01114   }
01115 
01116   return NS_OK;
01117 }
01118 
01122 /*
01123  * Gets the offset position of the caret (cursor).
01124  */
01125 NS_IMETHODIMP nsAccessibleEditableText::GetCaretOffset(PRInt32 *aCaretOffset)
01126 {
01127   *aCaretOffset = 0;
01128 
01129   PRInt32 startPos, endPos;
01130   nsresult rv = GetSelectionRange(&startPos, &endPos);
01131   NS_ENSURE_SUCCESS(rv, rv);
01132 
01133   if (startPos == endPos) { // selection must be collapsed
01134     *aCaretOffset = startPos;
01135     return NS_OK;
01136   }
01137 
01138   return NS_ERROR_FAILURE;
01139 }
01140 
01141 /*
01142  * Sets the caret (cursor) position to the specified offset.
01143  */
01144 NS_IMETHODIMP nsAccessibleEditableText::SetCaretOffset(PRInt32 aCaretOffset)
01145 {
01146   return SetSelectionRange(aCaretOffset, aCaretOffset);
01147 }
01148 
01149 /*
01150  * Gets the character count.
01151  */
01152 NS_IMETHODIMP nsAccessibleEditableText::GetCharacterCount(PRInt32 *aCharacterCount)
01153 {
01154   *aCharacterCount = 0;
01155 
01156   nsITextControlFrame *textFrame = GetTextFrame();
01157   if (textFrame) {
01158     return textFrame->GetTextLength(aCharacterCount);
01159   }
01160 
01161   NS_ENSURE_TRUE(mPlainEditor, NS_ERROR_FAILURE);
01162   NS_NAMED_LITERAL_STRING(format, "text/plain");
01163   nsAutoString text;
01164   mPlainEditor->OutputToString(format, nsIDocumentEncoder::OutputFormatted, text);
01165   *aCharacterCount = text.Length();
01166 
01167   return NS_OK;
01168 }
01169 
01170 /*
01171  * Gets the specified text.
01172  */
01173 NS_IMETHODIMP nsAccessibleEditableText::GetText(PRInt32 aStartOffset, PRInt32 aEndOffset, nsAString & aText)
01174 {
01175   if (aStartOffset == aEndOffset)
01176     return NS_OK;
01177 
01178   nsAutoString text;
01179   nsITextControlFrame *textFrame = GetTextFrame();
01180   if (textFrame) {
01181     textFrame->GetValue(text, PR_TRUE);
01182   }
01183   else {
01184     NS_ENSURE_TRUE(mPlainEditor, NS_ERROR_FAILURE);
01185     NS_NAMED_LITERAL_STRING(format, "text/plain");
01186     mPlainEditor->OutputToString(format, nsIDocumentEncoder::OutputFormatted, text);
01187   }
01188 
01189   PRInt32 length = text.Length();
01190   if (aEndOffset == -1) // get all text from aStartOffset
01191     aEndOffset = length;
01192 
01193   NS_ENSURE_TRUE((0 <= aStartOffset && aStartOffset < aEndOffset && aEndOffset <= length),
01194                  NS_ERROR_FAILURE);
01195 
01196   aText = Substring(text, aStartOffset, aEndOffset - aStartOffset);
01197 
01198   return NS_OK;
01199 }
01200 
01201 NS_IMETHODIMP nsAccessibleEditableText::GetTextBeforeOffset(PRInt32 aOffset, nsAccessibleTextBoundary aBoundaryType,
01202                                                     PRInt32 *aStartOffset, PRInt32 *aEndOffset, nsAString & aText)
01203 {
01204   return GetTextHelper(eGetBefore, aBoundaryType, aOffset, aStartOffset, aEndOffset, mPlainEditor, aText);
01205 }
01206 
01207 NS_IMETHODIMP nsAccessibleEditableText::GetTextAtOffset(PRInt32 aOffset, nsAccessibleTextBoundary aBoundaryType,
01208                                                 PRInt32 *aStartOffset, PRInt32 *aEndOffset, nsAString & aText)
01209 {
01210   return GetTextHelper(eGetAt, aBoundaryType, aOffset, aStartOffset, aEndOffset, mPlainEditor, aText);
01211 }
01212 
01213 NS_IMETHODIMP nsAccessibleEditableText::GetTextAfterOffset(PRInt32 aOffset, nsAccessibleTextBoundary aBoundaryType,
01214                                                    PRInt32 *aStartOffset, PRInt32 *aEndOffset, nsAString & aText)
01215 {
01216   return GetTextHelper(eGetAfter, aBoundaryType, aOffset, aStartOffset, aEndOffset, mPlainEditor, aText);
01217 }
01218 
01222 NS_IMETHODIMP nsAccessibleEditableText::SetAttributes(PRInt32 aStartPos, PRInt32 aEndPos, nsISupports *aAttributes)
01223 {
01224   return NS_ERROR_NOT_IMPLEMENTED;
01225 }
01226 
01227 NS_IMETHODIMP nsAccessibleEditableText::SetTextContents(const nsAString &aText)
01228 {
01229   nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea(do_QueryInterface(mTextNode));
01230   if (textArea)
01231     return textArea->SetValue(aText);
01232 
01233   nsCOMPtr<nsIDOMHTMLInputElement> inputElement(do_QueryInterface(mTextNode));
01234   if (inputElement)
01235     return inputElement->SetValue(aText);
01236 
01237   //XXX, editor doesn't support this method yet
01238 
01239   return NS_ERROR_FAILURE;
01240 }
01241 
01242 NS_IMETHODIMP nsAccessibleEditableText::InsertText(const nsAString &aText, PRInt32 aPosition)
01243 {
01244   if (NS_SUCCEEDED(SetSelectionRange(aPosition, aPosition))) {
01245     nsCOMPtr<nsIPlaintextEditor> peditor(do_QueryInterface(mPlainEditor));
01246     return peditor ? peditor->InsertText(aText) : NS_ERROR_FAILURE;
01247   }
01248 
01249   return NS_ERROR_FAILURE;
01250 }
01251 
01252 NS_IMETHODIMP nsAccessibleEditableText::CopyText(PRInt32 aStartPos, PRInt32 aEndPos)
01253 {
01254   if (mPlainEditor && NS_SUCCEEDED(SetSelectionRange(aStartPos, aEndPos)))
01255     return mPlainEditor->Copy();
01256 
01257   return NS_ERROR_FAILURE;
01258 }
01259 
01260 NS_IMETHODIMP nsAccessibleEditableText::CutText(PRInt32 aStartPos, PRInt32 aEndPos)
01261 {
01262   if (mPlainEditor && NS_SUCCEEDED(SetSelectionRange(aStartPos, aEndPos)))
01263     return mPlainEditor->Cut();
01264 
01265   return NS_ERROR_FAILURE;
01266 }
01267 
01268 NS_IMETHODIMP nsAccessibleEditableText::DeleteText(PRInt32 aStartPos, PRInt32 aEndPos)
01269 {
01270   if (mPlainEditor && NS_SUCCEEDED(SetSelectionRange(aStartPos, aEndPos)))
01271     return mPlainEditor->DeleteSelection(nsIEditor::eNone);
01272 
01273   return NS_ERROR_FAILURE;
01274 }
01275 
01276 NS_IMETHODIMP nsAccessibleEditableText::PasteText(PRInt32 aPosition)
01277 {
01278   if (mPlainEditor && NS_SUCCEEDED(SetSelectionRange(aPosition, aPosition)))
01279     return mPlainEditor->Paste(nsIClipboard::kGlobalClipboard);
01280 
01281   return NS_ERROR_FAILURE;
01282 }
01283 
01287 NS_IMETHODIMP nsAccessibleEditableText::WillCreateNode(const nsAString & aTag, nsIDOMNode *aParent, PRInt32 aPosition)
01288 {
01289   return NS_OK;
01290 }
01291 
01292 NS_IMETHODIMP nsAccessibleEditableText::DidCreateNode(const nsAString & aTag, nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aPosition, nsresult aResult)
01293 {
01294   return NS_OK;
01295 }
01296 
01297 NS_IMETHODIMP nsAccessibleEditableText::WillInsertNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aPosition)
01298 {
01299   return NS_OK;
01300 }
01301 
01302 NS_IMETHODIMP nsAccessibleEditableText::DidInsertNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aPosition, nsresult aResult)
01303 {
01304   AtkTextChange textData;
01305 
01306   nsCOMPtr<nsITextContent> textContent(do_QueryInterface(aNode));
01307   if (textContent) {
01308     textData.add = PR_TRUE;
01309     textData.length = textContent->TextLength();
01310     DOMPointToOffset(mPlainEditor, aNode, 0, &textData.start);
01311     FireTextChangeEvent(&textData);
01312   }
01313   return NS_OK;
01314 }
01315 
01316 NS_IMETHODIMP nsAccessibleEditableText::WillDeleteNode(nsIDOMNode *aChild)
01317 {
01318   AtkTextChange textData;
01319 
01320   textData.add = PR_FALSE;
01321   nsCOMPtr<nsITextContent> textContent(do_QueryInterface(aChild));
01322   if (textContent) {
01323     textData.length = textContent->TextLength();
01324   }
01325   else {
01326     //XXX, don't fire event for the last br
01327     nsCOMPtr<nsIDOMHTMLBRElement> br(do_QueryInterface(aChild));
01328     if (br)
01329       textData.length = 1;
01330     else
01331       return NS_OK;
01332   }
01333 
01334   DOMPointToOffset(mPlainEditor, aChild, 0, &textData.start);
01335   return FireTextChangeEvent(&textData);
01336 }
01337 
01338 NS_IMETHODIMP nsAccessibleEditableText::DidDeleteNode(nsIDOMNode *aChild, nsresult aResult)
01339 {
01340   return NS_OK;
01341 }
01342 
01343 NS_IMETHODIMP nsAccessibleEditableText::WillSplitNode(nsIDOMNode *aExistingRightNode, PRInt32 aOffset)
01344 {
01345   return NS_OK;
01346 }
01347 
01348 NS_IMETHODIMP nsAccessibleEditableText::DidSplitNode(nsIDOMNode *aExistingRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode, nsresult aResult)
01349 {
01350   return NS_OK;
01351 }
01352 
01353 NS_IMETHODIMP nsAccessibleEditableText::WillJoinNodes(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, nsIDOMNode *aParent)
01354 {
01355   return NS_OK;
01356 }
01357 
01358 NS_IMETHODIMP nsAccessibleEditableText::DidJoinNodes(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, nsIDOMNode *aParent, nsresult aResult)
01359 {
01360   return NS_OK;
01361 }
01362 
01363 NS_IMETHODIMP nsAccessibleEditableText::WillInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsAString & aString)
01364 {
01365   return NS_OK;
01366 }
01367 
01368 NS_IMETHODIMP nsAccessibleEditableText::DidInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsAString & aString, nsresult aResult)
01369 {
01370   AtkTextChange textData;
01371 
01372   textData.add = PR_TRUE;
01373   textData.length = aString.Length();
01374   DOMPointToOffset(mPlainEditor, aTextNode, aOffset, &textData.start);
01375   return FireTextChangeEvent(&textData);
01376 }
01377 
01378 NS_IMETHODIMP nsAccessibleEditableText::WillDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength)
01379 {
01380   AtkTextChange textData;
01381 
01382   textData.add = PR_FALSE;
01383   textData.length = aLength;
01384   DOMPointToOffset(mPlainEditor, aTextNode, aOffset, &textData.start);
01385   return FireTextChangeEvent(&textData);
01386 }
01387 
01388 NS_IMETHODIMP nsAccessibleEditableText::DidDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength, nsresult aResult)
01389 {
01390   return NS_OK;
01391 }
01392 
01393 NS_IMETHODIMP nsAccessibleEditableText::WillDeleteSelection(nsISelection *aSelection)
01394 // <input> & <textarea> fires this event while deleting text
01395 // <editor> fires WillDeleteText/WillDeleteNode instead
01396 {
01397   PRInt32 selectionStart, selectionEnd;
01398   nsresult rv = GetSelectionRange(&selectionStart, &selectionEnd);
01399   NS_ENSURE_SUCCESS(rv, rv);
01400 
01401   AtkTextChange textData;
01402 
01403   textData.add = PR_FALSE;
01404   textData.start = PR_MIN(selectionStart, selectionEnd);
01405   textData.length = PR_ABS(selectionEnd - selectionStart);
01406   return FireTextChangeEvent(&textData);
01407 }
01408 
01409 NS_IMETHODIMP nsAccessibleEditableText::DidDeleteSelection(nsISelection *aSelection)
01410 {
01411   return NS_OK;
01412 }
01413 
01414 // --------------------------------------------------------
01415 // nsTextAccessibleWrap Accessible
01416 // --------------------------------------------------------
01417 NS_IMPL_ISUPPORTS_INHERITED1(nsTextAccessibleWrap, nsTextAccessible, nsAccessibleText)
01418 
01419 nsTextAccessibleWrap::nsTextAccessibleWrap(nsIDOMNode* aDOMNode, nsIWeakReference* aShell):
01420 nsTextAccessible(aDOMNode, aShell), nsAccessibleText(aDOMNode)
01421 { 
01422 }