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