Back to index

lightning-sunbird  0.9+nobinonly
nsCaretAccessible.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; 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 // NOTE: alphabetically ordered
00039 #include "nsAccessibilityService.h"
00040 #include "nsCaretAccessible.h"
00041 #include "nsIAccessibleEvent.h"
00042 #include "nsICaret.h"
00043 #include "nsIDOMDocument.h"
00044 #include "nsIDOMHTMLAnchorElement.h"
00045 #include "nsIDOMHTMLInputElement.h"
00046 #include "nsIDOMHTMLTextAreaElement.h"
00047 #include "nsIFrame.h"
00048 #include "nsIPresShell.h"
00049 #include "nsISelectionController.h"
00050 #include "nsISelectionPrivate.h"
00051 #include "nsServiceManagerUtils.h"
00052 #include "nsIViewManager.h"
00053 #include "nsIWidget.h"
00054 #include "nsRootAccessible.h"
00055 #include "nsTextAccessible.h"
00056 
00057 #ifdef MOZ_ACCESSIBILITY_ATK
00058 #include "nsAccessibleText.h"
00059 #endif
00060 
00061 NS_IMPL_ISUPPORTS_INHERITED2(nsCaretAccessible, nsLeafAccessible, nsIAccessibleCaret, nsISelectionListener)
00062 
00063 nsCaretAccessible::nsCaretAccessible(nsIDOMNode* aDocumentNode, nsIWeakReference* aShell, nsRootAccessible *aRootAccessible):
00064 nsLeafAccessible(aDocumentNode, aShell), mVisible(PR_TRUE), mCurrentDOMNode(nsnull), mRootAccessible(aRootAccessible)
00065 {
00066   Init();
00067 }
00068 
00069 NS_IMETHODIMP nsCaretAccessible::Shutdown()
00070 {
00071   mDomSelectionWeak = nsnull;
00072   mCurrentDOMNode = nsnull;
00073   RemoveSelectionListener();
00074   return NS_OK;
00075 }
00076 
00077 NS_IMETHODIMP nsCaretAccessible::RemoveSelectionListener()
00078 {
00079   nsCOMPtr<nsISelection> prevDomSel(do_QueryReferent(mDomSelectionWeak));
00080   nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(prevDomSel));
00081   if (selPrivate) {
00082     mDomSelectionWeak = nsnull;
00083     return selPrivate->RemoveSelectionListener(this);
00084   }
00085   return NS_OK;
00086 }
00087 
00088 NS_IMETHODIMP nsCaretAccessible::AttachNewSelectionListener(nsIDOMNode *aCurrentNode)
00089 {
00090   mCurrentDOMNode = aCurrentNode;
00091 
00092   // When focus moves such that the caret is part of a new frame selection
00093   // this removes the old selection listener and attaches a new one for the current focus
00094   nsCOMPtr<nsIPresShell> presShell = 
00095     nsRootAccessible::GetPresShellFor(aCurrentNode);
00096   if (!presShell)
00097     return NS_ERROR_FAILURE;
00098 
00099   nsCOMPtr<nsIDocument> doc = presShell->GetDocument();
00100   if (!doc)  // we also should try to QI to document instead (necessary to do when node is a document)
00101     doc = do_QueryInterface(aCurrentNode);
00102   nsCOMPtr<nsIContent> content(do_QueryInterface(aCurrentNode));
00103   if (!content)
00104     content = doc->GetRootContent();  // If node is not content, use root content
00105 
00106   nsIFrame *frame = nsnull;
00107   presShell->GetPrimaryFrameFor(content, &frame);
00108   nsPresContext *presContext = presShell->GetPresContext();
00109   if (!frame || !presContext)
00110     return NS_ERROR_FAILURE;
00111 
00112   nsCOMPtr<nsISelectionController> selCon;
00113   frame->GetSelectionController(presContext, getter_AddRefs(selCon));
00114   if (!selCon)
00115     return NS_ERROR_FAILURE;
00116   
00117   nsCOMPtr<nsISelection> domSel, prevDomSel(do_QueryReferent(mDomSelectionWeak));
00118   selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(domSel));
00119   if (domSel == prevDomSel)
00120     return NS_OK; // This is already the selection we're listening to
00121   RemoveSelectionListener();
00122   nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(domSel));
00123 
00124   if (!selPrivate)
00125     return NS_ERROR_FAILURE;
00126 
00127   mDomSelectionWeak = do_GetWeakReference(domSel);
00128   return selPrivate->AddSelectionListener(this);
00129 }
00130 
00131 NS_IMETHODIMP nsCaretAccessible::NotifySelectionChanged(nsIDOMDocument *aDoc, nsISelection *aSel, PRInt16 aReason)
00132 {
00133 #ifdef MOZ_ACCESSIBILITY_ATK
00134   if (nsAccessibleText::gSuppressedNotifySelectionChanged)
00135     return NS_OK;
00136 #endif    
00137 
00138   nsCOMPtr<nsIPresShell> presShell = GetPresShellFor(mCurrentDOMNode);
00139   nsCOMPtr<nsISelection> domSel(do_QueryReferent(mDomSelectionWeak));
00140   if (!presShell || domSel != aSel)
00141     return NS_OK;  // Only listening to selection changes in currently focused frame
00142 
00143   nsCOMPtr<nsICaret> caret;
00144   presShell->GetCaret(getter_AddRefs(caret));
00145   if (!caret)
00146     return NS_OK;
00147 
00148   nsRect caretRect;
00149   PRBool isCollapsed;
00150   caret->GetCaretCoordinates(nsICaret::eTopLevelWindowCoordinates, domSel, &caretRect, &isCollapsed, nsnull);
00151 #ifndef MOZ_ACCESSIBILITY_ATK
00152   PRBool visible = (caretRect.x >= 0 && caretRect.y >= 0 && caretRect.width >= 0 && caretRect.height >= 0);
00153   if (visible)  // Make sure it's visible both by looking at coordinates and visible flag
00154     caret->GetCaretVisible(&visible);
00155   if (visible != mVisible) {
00156     mVisible = visible;
00157     mRootAccessible->FireToolkitEvent(mVisible? nsIAccessibleEvent::EVENT_SHOW: 
00158                                       nsIAccessibleEvent::EVENT_HIDE, this, nsnull);
00159   }
00160 
00161   nsPresContext *presContext = presShell->GetPresContext();
00162   nsIViewManager* viewManager = presShell->GetViewManager();
00163   if (!presContext || !viewManager)
00164     return NS_OK;
00165   nsIView *view = nsnull;
00166   viewManager->GetRootView(view);
00167   if (!view)
00168     return NS_OK;
00169   nsIWidget* widget = view->GetWidget();
00170   if (!widget)
00171     return NS_OK;
00172 
00173   float t2p;
00174   t2p = presContext->TwipsToPixels();
00175     // Convert to pixels using that scale
00176   caretRect.x      = NSTwipsToIntPixels(caretRect.x, t2p);
00177   caretRect.y      = NSTwipsToIntPixels(caretRect.y, t2p);
00178 
00179   caretRect.width  = NSTwipsToIntPixels(caretRect.width, t2p);
00180   caretRect.height = NSTwipsToIntPixels(caretRect.height, t2p);
00181 
00182   nsRect caretScreenRect;
00183   widget->WidgetToScreen(caretRect, mCaretRect);
00184 #endif
00185 
00186 #ifndef MOZ_ACCESSIBILITY_ATK
00187   mRootAccessible->FireToolkitEvent(nsIAccessibleEvent::EVENT_LOCATION_CHANGE, this, nsnull);
00188 #else
00189   nsCOMPtr<nsIAccessible> accessible;
00190   nsCOMPtr<nsIAccessibilityService> accService(do_GetService("@mozilla.org/accessibilityService;1"));
00191   accService->GetAccessibleInShell(mCurrentDOMNode, presShell, getter_AddRefs(accessible));
00192   nsCOMPtr<nsIAccessibleDocument> docAcc(do_QueryInterface(accessible));
00193   if (docAcc) {
00194     PRBool isEditable;
00195     docAcc->GetIsEditable(&isEditable);
00196     if (!isEditable) { // this is not a composer window, find out the text accessible object
00197       nsCOMPtr<nsIDOMNode> focusNode;
00198       domSel->GetFocusNode(getter_AddRefs(focusNode));
00199       if (!focusNode) {
00200         return NS_OK;
00201       }
00202       nsCOMPtr<nsIDOMHTMLAnchorElement> anchorElement(do_QueryInterface(focusNode));
00203       if (anchorElement) {
00204         // do not report caret-move event for link
00205         return NS_OK;
00206       }
00207       nsCOMPtr<nsIDOMNode> blockNode;
00208       if (NS_FAILED(nsAccessible::GetParentBlockNode(presShell, focusNode, getter_AddRefs(blockNode)))) {
00209         return NS_OK;
00210       }
00211       accService->GetAccessibleInShell(blockNode, presShell, getter_AddRefs(accessible));
00212       if (!accessible) {
00213         return NS_OK;
00214       }
00215     }
00216   }
00217 
00218   if (!accessible) {
00219     return NS_OK;
00220   }
00221 
00222   if (isCollapsed) {
00223     nsCOMPtr<nsIAccessibleText> textAcc(do_QueryInterface(accessible));
00224     if (textAcc) {
00225       PRInt32 caretOffset;
00226       textAcc->GetCaretOffset(&caretOffset);
00227       mRootAccessible->FireToolkitEvent(nsIAccessibleEvent::EVENT_ATK_TEXT_CARET_MOVE, accessible, &caretOffset);
00228     }
00229   }
00230   else {
00231     //Current text interface doesn't support this event yet
00232     //mListener->FireToolkitEvent(nsIAccessibleEventReceiver::EVENT_ATK_TEXT_SELECTION_CHANGE, accessible, nsnull);
00233   }
00234 #endif
00235 
00236   return NS_OK;
00237 }
00238 
00240 NS_IMETHODIMP nsCaretAccessible::GetBounds(PRInt32 *x, PRInt32 *y, PRInt32 *width, PRInt32 *height)
00241 {
00242   if (mCaretRect.IsEmpty()) {
00243     return NS_ERROR_FAILURE;
00244   }
00245   *x = mCaretRect.x;
00246   *y = mCaretRect.y;
00247   *width = mCaretRect.width;
00248   *height = mCaretRect.height;
00249   return NS_OK;
00250 }
00251 
00252 NS_IMETHODIMP nsCaretAccessible::GetRole(PRUint32 *_retval)
00253 {
00254   *_retval = ROLE_CARET;
00255   return NS_OK;
00256 }
00257 
00258 NS_IMETHODIMP nsCaretAccessible::GetState(PRUint32 *_retval)
00259 {
00260   *_retval = mVisible? 0: STATE_INVISIBLE;
00261   return NS_OK;
00262 }
00263 
00264 NS_IMETHODIMP nsCaretAccessible::GetParent(nsIAccessible **_retval)
00265 {   
00266   *_retval = nsnull;
00267   return NS_OK;
00268 }
00269 NS_IMETHODIMP nsCaretAccessible::GetPreviousSibling(nsIAccessible **_retval)
00270 { 
00271   *_retval = nsnull;
00272   return NS_OK;
00273 }
00274 
00275 NS_IMETHODIMP nsCaretAccessible::GetNextSibling(nsIAccessible **_retval)
00276 {
00277   *_retval = nsnull;
00278   return NS_OK;
00279 }
00280