xref: /trunk/main/sc/source/ui/Accessibility/AccessibleCell.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sc.hxx"
30 
31 
32 #include "AccessibleCell.hxx"
33 #include "scitems.hxx"
34 #include <editeng/eeitem.hxx>
35 
36 
37 #include "AccessibleText.hxx"
38 #include "AccessibleDocument.hxx"
39 #include "tabvwsh.hxx"
40 #include "document.hxx"
41 #include "attrib.hxx"
42 #include "miscuno.hxx"
43 #include "unoguard.hxx"
44 #include "editsrc.hxx"
45 #include "dociter.hxx"
46 #include "cell.hxx"
47 
48 #ifndef _UTL_ACCESSIBLESTATESETHELPER_HXX
49 #include <unotools/accessiblestatesethelper.hxx>
50 #endif
51 #ifndef _COM_SUN_STAR_ACCESSIBILITY_XACCESSIBLEROLE_HPP_
52 #include <com/sun/star/accessibility/AccessibleRole.hpp>
53 #endif
54 #ifndef _COM_SUN_STAR_ACCESSIBILITY_XACCESSIBLESTATETYPE_HPP_
55 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
56 #endif
57 #include <com/sun/star/accessibility/AccessibleRelationType.hpp>
58 #include <com/sun/star/accessibility/XAccessibleTable.hpp>
59 #include <rtl/uuid.h>
60 #include <tools/debug.hxx>
61 #include <editeng/brshitem.hxx>
62 #include <comphelper/sequence.hxx>
63 #include <float.h>
64 
65 using namespace ::com::sun::star;
66 using namespace ::com::sun::star::accessibility;
67 
68 //=====  internal  ============================================================
69 
70 ScAccessibleCell::ScAccessibleCell(
71         const uno::Reference<XAccessible>& rxParent,
72         ScTabViewShell* pViewShell,
73         ScAddress& rCellAddress,
74         sal_Int32 nIndex,
75         ScSplitPos eSplitPos,
76         ScAccessibleDocument* pAccDoc)
77     :
78     ScAccessibleCellBase(rxParent, GetDocument(pViewShell), rCellAddress, nIndex),
79         ::accessibility::AccessibleStaticTextBase(CreateEditSource(pViewShell, rCellAddress, eSplitPos)),
80     mpViewShell(pViewShell),
81     mpAccDoc(pAccDoc),
82     meSplitPos(eSplitPos)
83 {
84     if (pViewShell)
85         pViewShell->AddAccessibilityObject(*this);
86 }
87 
88 ScAccessibleCell::~ScAccessibleCell()
89 {
90     if (!ScAccessibleContextBase::IsDefunc() && !rBHelper.bInDispose)
91     {
92         // increment refcount to prevent double call off dtor
93         osl_incrementInterlockedCount( &m_refCount );
94         // call dispose to inform object wich have a weak reference to this object
95         dispose();
96     }
97 }
98 
99 void ScAccessibleCell::Init()
100 {
101     ScAccessibleCellBase::Init();
102 
103     SetEventSource(this);
104 }
105 
106 void SAL_CALL ScAccessibleCell::disposing()
107 {
108     ScUnoGuard aGuard;
109     // #100593# dispose in AccessibleStaticTextBase
110     Dispose();
111 
112     if (mpViewShell)
113     {
114         mpViewShell->RemoveAccessibilityObject(*this);
115         mpViewShell = NULL;
116     }
117     mpAccDoc = NULL;
118 
119     ScAccessibleCellBase::disposing();
120 }
121 
122     //=====  XInterface  =====================================================
123 
124 IMPLEMENT_FORWARD_XINTERFACE2( ScAccessibleCell, ScAccessibleCellBase, AccessibleStaticTextBase )
125 
126     //=====  XTypeProvider  ===================================================
127 
128 IMPLEMENT_FORWARD_XTYPEPROVIDER2( ScAccessibleCell, ScAccessibleCellBase, AccessibleStaticTextBase )
129 
130     //=====  XAccessibleComponent  ============================================
131 
132 uno::Reference< XAccessible > SAL_CALL ScAccessibleCell::getAccessibleAtPoint(
133         const awt::Point& rPoint )
134         throw (uno::RuntimeException)
135 {
136     return AccessibleStaticTextBase::getAccessibleAtPoint(rPoint);
137 }
138 
139 void SAL_CALL ScAccessibleCell::grabFocus(  )
140         throw (uno::RuntimeException)
141 {
142     ScUnoGuard aGuard;
143     IsObjectValid();
144     if (getAccessibleParent().is() && mpViewShell)
145     {
146         uno::Reference<XAccessibleComponent> xAccessibleComponent(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY);
147         if (xAccessibleComponent.is())
148         {
149             xAccessibleComponent->grabFocus();
150             mpViewShell->SetCursor(maCellAddress.Col(), maCellAddress.Row());
151         }
152     }
153 }
154 
155 Rectangle ScAccessibleCell::GetBoundingBoxOnScreen(void) const
156         throw (uno::RuntimeException)
157 {
158     Rectangle aCellRect(GetBoundingBox());
159     if (mpViewShell)
160     {
161         Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
162         if (pWindow)
163         {
164             Rectangle aRect = pWindow->GetWindowExtentsRelative(NULL);
165             aCellRect.setX(aCellRect.getX() + aRect.getX());
166             aCellRect.setY(aCellRect.getY() + aRect.getY());
167         }
168     }
169     return aCellRect;
170 }
171 
172 Rectangle ScAccessibleCell::GetBoundingBox(void) const
173         throw (uno::RuntimeException)
174 {
175     Rectangle aCellRect;
176     if (mpViewShell)
177     {
178         long nSizeX, nSizeY;
179         mpViewShell->GetViewData()->GetMergeSizePixel(
180             maCellAddress.Col(), maCellAddress.Row(), nSizeX, nSizeY);
181         aCellRect.SetSize(Size(nSizeX, nSizeY));
182         aCellRect.SetPos(mpViewShell->GetViewData()->GetScrPos(maCellAddress.Col(), maCellAddress.Row(), meSplitPos, sal_True));
183 
184         Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
185         if (pWindow)
186         {
187             Rectangle aRect(pWindow->GetWindowExtentsRelative(pWindow->GetAccessibleParentWindow()));
188             aRect.Move(-aRect.Left(), -aRect.Top());
189             aCellRect = aRect.Intersection(aCellRect);
190         }
191 
192         /*  #i19430# Gnopernicus reads text partly if it sticks out of the cell
193             boundaries. This leads to wrong results in cases where the cell
194             text is rotated, because rotation is not taken into account when
195             calculating the visible part of the text. In these cases we will
196             simply expand the cell size to the width of the unrotated text. */
197         if (mpDoc)
198         {
199             const SfxInt32Item* pItem = static_cast< const SfxInt32Item* >(
200                 mpDoc->GetAttr( maCellAddress.Col(), maCellAddress.Row(), maCellAddress.Tab(), ATTR_ROTATE_VALUE ) );
201             if( pItem && (pItem->GetValue() != 0) )
202             {
203                 Rectangle aParaRect = GetParagraphBoundingBox();
204                 if( !aParaRect.IsEmpty() && (aCellRect.GetWidth() < aParaRect.GetWidth()) )
205                     aCellRect.SetSize( Size( aParaRect.GetWidth(), aCellRect.GetHeight() ) );
206             }
207         }
208     }
209     if (aCellRect.IsEmpty())
210         aCellRect.SetPos(Point(-1, -1));
211     return aCellRect;
212 }
213 
214     //=====  XAccessibleContext  ==============================================
215 
216 sal_Int32 SAL_CALL
217     ScAccessibleCell::getAccessibleChildCount(void)
218                     throw (uno::RuntimeException)
219 {
220     return AccessibleStaticTextBase::getAccessibleChildCount();
221 }
222 
223 uno::Reference< XAccessible > SAL_CALL
224     ScAccessibleCell::getAccessibleChild(sal_Int32 nIndex)
225         throw (uno::RuntimeException,
226         lang::IndexOutOfBoundsException)
227 {
228     return AccessibleStaticTextBase::getAccessibleChild(nIndex);
229 }
230 
231 uno::Reference<XAccessibleStateSet> SAL_CALL
232     ScAccessibleCell::getAccessibleStateSet(void)
233     throw (uno::RuntimeException)
234 {
235     ScUnoGuard aGuard;
236     uno::Reference<XAccessibleStateSet> xParentStates;
237     if (getAccessibleParent().is())
238     {
239         uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
240         xParentStates = xParentContext->getAccessibleStateSet();
241     }
242     utl::AccessibleStateSetHelper* pStateSet = new utl::AccessibleStateSetHelper();
243     if (IsDefunc(xParentStates))
244         pStateSet->AddState(AccessibleStateType::DEFUNC);
245     else
246     {
247         if (IsEditable(xParentStates))
248         {
249             pStateSet->AddState(AccessibleStateType::EDITABLE);
250             pStateSet->AddState(AccessibleStateType::RESIZABLE);
251         }
252         pStateSet->AddState(AccessibleStateType::ENABLED);
253         pStateSet->AddState(AccessibleStateType::MULTI_LINE);
254         pStateSet->AddState(AccessibleStateType::MULTI_SELECTABLE);
255         if (IsOpaque(xParentStates))
256             pStateSet->AddState(AccessibleStateType::OPAQUE);
257         pStateSet->AddState(AccessibleStateType::SELECTABLE);
258         if (IsSelected())
259             pStateSet->AddState(AccessibleStateType::SELECTED);
260         if (isShowing())
261             pStateSet->AddState(AccessibleStateType::SHOWING);
262         pStateSet->AddState(AccessibleStateType::TRANSIENT);
263         if (isVisible())
264             pStateSet->AddState(AccessibleStateType::VISIBLE);
265     }
266     return pStateSet;
267 }
268 
269 uno::Reference<XAccessibleRelationSet> SAL_CALL
270     ScAccessibleCell::getAccessibleRelationSet(void)
271     throw (uno::RuntimeException)
272 {
273     ScUnoGuard aGuard;
274     IsObjectValid();
275     utl::AccessibleRelationSetHelper* pRelationSet = NULL;
276     if (mpAccDoc)
277         pRelationSet = mpAccDoc->GetRelationSet(&maCellAddress);
278     if (!pRelationSet)
279         pRelationSet = new utl::AccessibleRelationSetHelper();
280     FillDependends(pRelationSet);
281     FillPrecedents(pRelationSet);
282     return pRelationSet;
283 }
284 
285     //=====  XServiceInfo  ====================================================
286 
287 ::rtl::OUString SAL_CALL ScAccessibleCell::getImplementationName(void)
288         throw (uno::RuntimeException)
289 {
290     return rtl::OUString(RTL_CONSTASCII_USTRINGPARAM ("ScAccessibleCell"));
291 }
292 
293 uno::Sequence< ::rtl::OUString> SAL_CALL
294     ScAccessibleCell::getSupportedServiceNames(void)
295         throw (uno::RuntimeException)
296 {
297     uno::Sequence< ::rtl::OUString > aSequence = ScAccessibleContextBase::getSupportedServiceNames();
298     sal_Int32 nOldSize(aSequence.getLength());
299     aSequence.realloc(nOldSize + 1);
300     ::rtl::OUString* pNames = aSequence.getArray();
301 
302     pNames[nOldSize] = rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.sheet.AccessibleCell"));
303 
304     return aSequence;
305 }
306 
307     //====  internal  =========================================================
308 
309 sal_Bool ScAccessibleCell::IsDefunc(
310     const uno::Reference<XAccessibleStateSet>& rxParentStates)
311 {
312     return ScAccessibleContextBase::IsDefunc() || (mpDoc == NULL) || (mpViewShell == NULL) || !getAccessibleParent().is() ||
313          (rxParentStates.is() && rxParentStates->contains(AccessibleStateType::DEFUNC));
314 }
315 
316 sal_Bool ScAccessibleCell::IsEditable(
317     const uno::Reference<XAccessibleStateSet>& rxParentStates)
318 {
319     sal_Bool bEditable(sal_True);
320     if (rxParentStates.is() && !rxParentStates->contains(AccessibleStateType::EDITABLE) &&
321         mpDoc)
322     {
323         // here I have to test whether the protection of the table should influence this cell.
324         const ScProtectionAttr* pItem = (const ScProtectionAttr*)mpDoc->GetAttr(
325             maCellAddress.Col(), maCellAddress.Row(),
326             maCellAddress.Tab(), ATTR_PROTECTION);
327         if (pItem)
328             bEditable = !pItem->GetProtection();
329     }
330     return bEditable;
331 }
332 
333 sal_Bool ScAccessibleCell::IsOpaque(
334     const uno::Reference<XAccessibleStateSet>& /* rxParentStates */)
335 {
336     // test whether there is a background color
337     sal_Bool bOpaque(sal_True);
338     if (mpDoc)
339     {
340         const SvxBrushItem* pItem = (const SvxBrushItem*)mpDoc->GetAttr(
341             maCellAddress.Col(), maCellAddress.Row(),
342             maCellAddress.Tab(), ATTR_BACKGROUND);
343         if (pItem)
344             bOpaque = pItem->GetColor() != COL_TRANSPARENT;
345     }
346     return bOpaque;
347 }
348 
349 sal_Bool ScAccessibleCell::IsSelected()
350 {
351     sal_Bool bResult(sal_False);
352     if (mpViewShell && mpViewShell->GetViewData())
353     {
354         const ScMarkData& rMarkdata = mpViewShell->GetViewData()->GetMarkData();
355         bResult = rMarkdata.IsCellMarked(maCellAddress.Col(), maCellAddress.Row());
356     }
357     return bResult;
358 }
359 
360 ScDocument* ScAccessibleCell::GetDocument(ScTabViewShell* pViewShell)
361 {
362     ScDocument* pDoc = NULL;
363     if (pViewShell && pViewShell->GetViewData())
364         pDoc = pViewShell->GetViewData()->GetDocument();
365     return pDoc;
366 }
367 
368 ::std::auto_ptr< SvxEditSource > ScAccessibleCell::CreateEditSource(ScTabViewShell* pViewShell, ScAddress aCell, ScSplitPos eSplitPos)
369 {
370     ::std::auto_ptr < ScAccessibleTextData > pAccessibleCellTextData
371         ( new ScAccessibleCellTextData( pViewShell, aCell, eSplitPos, this ) );
372     ::std::auto_ptr< SvxEditSource > pEditSource (new ScAccessibilityEditSource(pAccessibleCellTextData));
373 
374     return pEditSource;
375 }
376 
377 void ScAccessibleCell::FillDependends(utl::AccessibleRelationSetHelper* pRelationSet)
378 {
379     if (mpDoc)
380     {
381         ScCellIterator aCellIter( mpDoc, 0,0, maCellAddress.Tab(), MAXCOL,MAXROW, maCellAddress.Tab() );
382         ScBaseCell* pCell = aCellIter.GetFirst();
383         while (pCell)
384         {
385             if (pCell->GetCellType() == CELLTYPE_FORMULA)
386             {
387                 sal_Bool bFound(sal_False);
388                 ScDetectiveRefIter aIter( (ScFormulaCell*) pCell );
389                 ScRange aRef;
390                 while ( !bFound && aIter.GetNextRef( aRef ) )
391                 {
392                     if (aRef.In(maCellAddress))
393                         bFound = sal_True;
394                 }
395                 if (bFound)
396                     AddRelation(ScAddress(aCellIter.GetCol(), aCellIter.GetRow(), aCellIter.GetTab()), AccessibleRelationType::CONTROLLER_FOR, pRelationSet);
397             }
398             pCell = aCellIter.GetNext();
399         }
400     }
401 }
402 
403 void ScAccessibleCell::FillPrecedents(utl::AccessibleRelationSetHelper* pRelationSet)
404 {
405     if (mpDoc)
406     {
407         ScBaseCell* pBaseCell = mpDoc->GetCell(maCellAddress);
408         if (pBaseCell && (pBaseCell->GetCellType() == CELLTYPE_FORMULA))
409         {
410             ScFormulaCell* pFCell = (ScFormulaCell*) pBaseCell;
411 
412             ScDetectiveRefIter aIter( pFCell );
413             ScRange aRef;
414             while ( aIter.GetNextRef( aRef ) )
415             {
416                 AddRelation( aRef, AccessibleRelationType::CONTROLLED_BY, pRelationSet);
417             }
418         }
419     }
420 }
421 
422 void ScAccessibleCell::AddRelation(const ScAddress& rCell,
423     const sal_uInt16 aRelationType,
424     utl::AccessibleRelationSetHelper* pRelationSet)
425 {
426     AddRelation(ScRange(rCell, rCell), aRelationType, pRelationSet);
427 }
428 
429 void ScAccessibleCell::AddRelation(const ScRange& rRange,
430     const sal_uInt16 aRelationType,
431     utl::AccessibleRelationSetHelper* pRelationSet)
432 {
433     uno::Reference < XAccessibleTable > xTable ( getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY );
434     if (xTable.is())
435     {
436         sal_uInt32 nCount(static_cast<sal_uInt32>(rRange.aEnd.Col() -
437                     rRange.aStart.Col() + 1) * (rRange.aEnd.Row() -
438                     rRange.aStart.Row() + 1));
439         uno::Sequence < uno::Reference < uno::XInterface > > aTargetSet( nCount );
440         uno::Reference < uno::XInterface >* pTargetSet = aTargetSet.getArray();
441         if (pTargetSet)
442         {
443             sal_uInt32 nPos(0);
444             for (sal_uInt32 nRow = rRange.aStart.Row(); nRow <= sal::static_int_cast<sal_uInt32>(rRange.aEnd.Row()); ++nRow)
445             {
446                 for (sal_uInt32 nCol = rRange.aStart.Col(); nCol <= sal::static_int_cast<sal_uInt32>(rRange.aEnd.Col()); ++nCol)
447                 {
448                     pTargetSet[nPos] = xTable->getAccessibleCellAt(nRow, nCol);
449                     ++nPos;
450                 }
451             }
452             DBG_ASSERT(nCount == nPos, "something wents wrong");
453         }
454         AccessibleRelation aRelation;
455         aRelation.RelationType = aRelationType;
456         aRelation.TargetSet = aTargetSet;
457         pRelationSet->AddRelation(aRelation);
458     }
459 }
460