Back to index

lightning-sunbird  0.9+nobinonly
nsTextAccessibleWrap.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 Netscape Corp.
00018  * Portions created by Netscape Corp.are Copyright (C) 2003 Netscape
00019  * Corp. All Rights Reserved.
00020  *
00021  * Original Author: Aaron Leventhal
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 "nsTextAccessibleWrap.h"
00042 #include "ISimpleDOMText_i.c"
00043 #include "nsContentCID.h"
00044 #include "nsIAccessibleDocument.h"
00045 #include "nsIDOMRange.h"
00046 #include "nsIFontMetrics.h"
00047 #include "nsIFrame.h"
00048 #include "nsPresContext.h"
00049 #include "nsIPresShell.h"
00050 #include "nsIRenderingContext.h"
00051 #include "nsISelection.h"
00052 #include "nsISelectionController.h"
00053 #include "nsIViewManager.h"
00054 #include "nsIWidget.h"
00055 
00056 static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
00057 
00058 // --------------------------------------------------------
00059 // nsTextAccessibleWrap Accessible
00060 // --------------------------------------------------------
00061 
00062 nsTextAccessibleWrap::nsTextAccessibleWrap(nsIDOMNode* aDOMNode, nsIWeakReference* aShell):
00063 nsTextAccessible(aDOMNode, aShell)
00064 { 
00065 }
00066 
00067 STDMETHODIMP_(ULONG) nsTextAccessibleWrap::AddRef()
00068 {
00069   return nsAccessNode::AddRef();
00070 }
00071 
00072 STDMETHODIMP_(ULONG) nsTextAccessibleWrap::Release()
00073 {
00074   return nsAccessNode::Release();
00075 }
00076 
00077 STDMETHODIMP nsTextAccessibleWrap::QueryInterface(REFIID iid, void** ppv)
00078 {
00079   *ppv = nsnull;
00080 
00081   if (IID_IUnknown == iid || IID_ISimpleDOMText == iid)
00082     *ppv = NS_STATIC_CAST(ISimpleDOMText*, this);
00083 
00084   if (nsnull == *ppv)
00085     return nsAccessibleWrap::QueryInterface(iid, ppv);
00086    
00087   (NS_REINTERPRET_CAST(IUnknown*, *ppv))->AddRef(); 
00088   return S_OK;
00089 }
00090 
00091 STDMETHODIMP nsTextAccessibleWrap::get_domText( 
00092     /* [retval][out] */ BSTR __RPC_FAR *aDomText)
00093 {
00094   if (!mDOMNode) {
00095     return E_FAIL; // Node already shut down
00096   }
00097   nsAutoString nodeValue;
00098 
00099   mDOMNode->GetNodeValue(nodeValue);
00100   *aDomText = ::SysAllocString(nodeValue.get());
00101 
00102   return S_OK;
00103 }
00104 
00105 STDMETHODIMP nsTextAccessibleWrap::get_clippedSubstringBounds( 
00106     /* [in] */ unsigned int aStartIndex,
00107     /* [in] */ unsigned int aEndIndex,
00108     /* [out] */ int __RPC_FAR *aX,
00109     /* [out] */ int __RPC_FAR *aY,
00110     /* [out] */ int __RPC_FAR *aWidth,
00111     /* [out] */ int __RPC_FAR *aHeight)
00112 {
00113   nscoord x, y, width, height, docX, docY, docWidth, docHeight;
00114   HRESULT rv = get_unclippedSubstringBounds(aStartIndex, aEndIndex, &x, &y, &width, &height);
00115   if (FAILED(rv)) {
00116     return rv;
00117   }
00118 
00119   nsCOMPtr<nsIAccessibleDocument> docAccessible(GetDocAccessible());
00120   nsCOMPtr<nsIAccessible> accessible(do_QueryInterface(docAccessible));
00121   NS_ASSERTION(accessible, "There must always be a doc accessible, but there isn't");
00122 
00123   accessible->GetBounds(&docX, &docY, &docWidth, &docHeight);
00124 
00125   nsRect unclippedRect(x, y, width, height);
00126   nsRect docRect(docX, docY, docWidth, docHeight);
00127   nsRect clippedRect;
00128 
00129   clippedRect.IntersectRect(unclippedRect, docRect);
00130 
00131   *aX = clippedRect.x;
00132   *aY = clippedRect.y;
00133   *aWidth = clippedRect.width;
00134   *aHeight = clippedRect.height;
00135 
00136   return S_OK;
00137 }
00138 
00139 STDMETHODIMP nsTextAccessibleWrap::get_unclippedSubstringBounds( 
00140     /* [in] */ unsigned int aStartIndex,
00141     /* [in] */ unsigned int aEndIndex,
00142     /* [out] */ int __RPC_FAR *aX,
00143     /* [out] */ int __RPC_FAR *aY,
00144     /* [out] */ int __RPC_FAR *aWidth,
00145     /* [out] */ int __RPC_FAR *aHeight)
00146 {
00147   if (!mDOMNode) {
00148     return E_FAIL; // Node already shut down
00149   }
00150 
00151   if (NS_FAILED(GetCharacterExtents(aStartIndex, aEndIndex, 
00152                                     aX, aY, aWidth, aHeight))) {
00153     return NS_ERROR_FAILURE;
00154   }
00155 
00156   return S_OK;
00157 }
00158 
00159 
00160 STDMETHODIMP nsTextAccessibleWrap::scrollToSubstring( 
00161     /* [in] */ unsigned int aStartIndex,
00162     /* [in] */ unsigned int aEndIndex)
00163 {
00164   nsCOMPtr<nsIPresShell> presShell(GetPresShell());
00165   nsIFrame *frame = GetFrame();
00166 
00167   if (!frame || !presShell) {
00168     return E_FAIL;  // This accessible has been shut down
00169   }
00170 
00171   nsPresContext *presContext = presShell->GetPresContext();
00172   nsCOMPtr<nsIDOMRange> scrollToRange = do_CreateInstance(kRangeCID);
00173   nsCOMPtr<nsISelectionController> selCon;
00174   frame->GetSelectionController(presContext, getter_AddRefs(selCon));
00175   if (!presContext || !scrollToRange || !selCon) {
00176     return E_FAIL;
00177   }
00178 
00179   scrollToRange->SetStart(mDOMNode, aStartIndex);
00180   scrollToRange->SetEnd(mDOMNode, aEndIndex);
00181   nsCOMPtr<nsISelection> domSel;
00182   selCon->GetSelection(nsISelectionController::SELECTION_ACCESSIBILITY, 
00183                        getter_AddRefs(domSel));
00184   if (domSel) {
00185     domSel->RemoveAllRanges();
00186     domSel->AddRange(scrollToRange);
00187 
00188     selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_ACCESSIBILITY, 
00189       nsISelectionController::SELECTION_ANCHOR_REGION, PR_TRUE);
00190 
00191     domSel->CollapseToStart();
00192   }
00193 
00194   return S_OK;
00195 }
00196 
00197 nsIFrame* nsTextAccessibleWrap::GetPointFromOffset(nsIFrame *aContainingFrame, 
00198                                                    nsPresContext *aPresContext,
00199                                                    nsIRenderingContext *aRendContext,
00200                                                    PRInt32 aOffset, 
00201                                                    PRBool aPreferNext, 
00202                                                    nsPoint& aOutPoint)
00203 {
00204   nsIFrame *textFrame = nsnull;
00205   PRInt32 outOffset;
00206   aContainingFrame->GetChildFrameContainingOffset(aOffset, aPreferNext, &outOffset, &textFrame);
00207   if (!textFrame) {
00208     return nsnull;
00209   }
00210 
00211   textFrame->GetPointFromOffset(aPresContext, aRendContext, aOffset, &aOutPoint);
00212 
00213   return textFrame;
00214 }
00215 
00216 /*
00217  * Given an offset, the x, y, width, and height values are filled appropriately.
00218  */
00219 nsresult nsTextAccessibleWrap::GetCharacterExtents(PRInt32 aStartOffset, PRInt32 aEndOffset,
00220                                                    PRInt32* aX, PRInt32* aY, 
00221                                                    PRInt32* aWidth, PRInt32* aHeight) 
00222 {
00223   nsCOMPtr<nsIPresShell> presShell(GetPresShell());
00224   NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
00225 
00226   nsPresContext *presContext = presShell->GetPresContext();
00227   NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
00228   float t2p = presContext->TwipsToPixels();
00229 
00230   nsIFrame *frame = GetFrame();
00231   NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
00232 
00233   nsIWidget *widget = frame->GetWindow();
00234   NS_ENSURE_TRUE(widget, NS_ERROR_FAILURE);
00235 
00236   nsCOMPtr<nsIRenderingContext>
00237     rendContext(getter_AddRefs(widget->GetRenderingContext()));
00238 
00239   nsPoint startPoint, endPoint;
00240   nsIFrame *startFrame = GetPointFromOffset(frame, presContext, rendContext, 
00241                                             aStartOffset, PR_TRUE, startPoint);
00242   nsIFrame *endFrame = GetPointFromOffset(frame, presContext, rendContext, 
00243                                           aEndOffset, PR_FALSE, endPoint);
00244   if (!startFrame || !endFrame) {
00245     return E_FAIL;
00246   }
00247   
00248   nsRect sum(0, 0, 0, 0);
00249   nsIFrame *iter = startFrame;
00250   nsIFrame *stopLoopFrame = endFrame->GetNextInFlow();
00251   for (; iter != stopLoopFrame; iter = iter->GetNextInFlow()) {
00252     nsRect rect = iter->GetScreenRectExternal();
00253     nscoord start = (iter == startFrame) ? NSTwipsToIntPixels(startPoint.x, t2p) : 0;
00254     nscoord end = (iter == endFrame) ? NSTwipsToIntPixels(endPoint.x, t2p) :
00255                                        rect.width;
00256     rect.x += start;
00257     rect.width = end - start;
00258     sum.UnionRect(sum, rect);
00259   }
00260 
00261   *aX      = sum.x;
00262   *aY      = sum.y;
00263   *aWidth  = sum.width;
00264   *aHeight = sum.height;
00265 
00266   return NS_OK;
00267 }
00268 
00269 STDMETHODIMP nsTextAccessibleWrap::get_fontFamily(
00270     /* [retval][out] */ BSTR __RPC_FAR *aFontFamily)
00271 {
00272   nsIFrame *frame = GetFrame();
00273   nsCOMPtr<nsIPresShell> presShell = GetPresShell();
00274   if (!frame || !presShell) {
00275     return E_FAIL;
00276   }
00277 
00278   nsCOMPtr<nsIRenderingContext> rc;
00279   presShell->CreateRenderingContext(frame, getter_AddRefs(rc));
00280   if (!rc) {
00281     return E_FAIL;
00282   }
00283 
00284   const nsStyleFont *font = frame->GetStyleFont();
00285 
00286   const nsStyleVisibility *visibility = frame->GetStyleVisibility();
00287 
00288   if (NS_FAILED(rc->SetFont(font->mFont, visibility->mLangGroup))) {
00289     return E_FAIL;
00290   }
00291 
00292   nsCOMPtr<nsIDeviceContext> deviceContext;
00293   rc->GetDeviceContext(*getter_AddRefs(deviceContext));
00294   if (!deviceContext) {
00295     return E_FAIL;
00296   }
00297 
00298   nsIFontMetrics *fm;
00299   rc->GetFontMetrics(fm);
00300   if (!fm) {
00301     return E_FAIL;
00302   }
00303 
00304   nsAutoString fontFamily;
00305   deviceContext->FirstExistingFont(fm->Font(), fontFamily);
00306   
00307   *aFontFamily = ::SysAllocString(fontFamily.get());
00308   return S_OK;
00309 }