1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
24 #include "AccessibleCell.hxx"
25 #include "scitems.hxx"
26 #include <editeng/eeitem.hxx>
28 #include "AccessibleText.hxx"
29 #include "AccessibleDocument.hxx"
30 #include "tabvwsh.hxx"
31 #include "document.hxx"
33 #include "miscuno.hxx"
34 #include "editsrc.hxx"
35 #include "dociter.hxx"
36 #include "markdata.hxx"
37 #include "cellvalue.hxx"
38 #include "formulaiter.hxx"
39 #include "validat.hxx"
41 #include <unotools/accessiblestatesethelper.hxx>
42 #include <com/sun/star/accessibility/AccessibleRole.hpp>
43 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
44 #include <com/sun/star/accessibility/AccessibleRelationType.hpp>
45 #include <com/sun/star/accessibility/XAccessibleTable.hpp>
46 #include <editeng/brushitem.hxx>
47 #include <comphelper/sequence.hxx>
49 #include <vcl/svapp.hxx>
51 #include "AccessibleSpreadsheet.hxx"
53 using namespace ::com::sun::star
;
54 using namespace ::com::sun::star::accessibility
;
56 rtl::Reference
<ScAccessibleCell
> ScAccessibleCell::create(
57 const uno::Reference
<XAccessible
>& rxParent
,
58 ScTabViewShell
* pViewShell
,
59 ScAddress
& rCellAddress
,
62 ScAccessibleDocument
* pAccDoc
)
64 rtl::Reference
<ScAccessibleCell
> x(new ScAccessibleCell(
65 rxParent
, pViewShell
, rCellAddress
, nIndex
, eSplitPos
, pAccDoc
));
70 ScAccessibleCell::ScAccessibleCell(
71 const uno::Reference
<XAccessible
>& rxParent
,
72 ScTabViewShell
* pViewShell
,
73 ScAddress
& rCellAddress
,
76 ScAccessibleDocument
* pAccDoc
)
78 ScAccessibleCellBase(rxParent
, GetDocument(pViewShell
), rCellAddress
, nIndex
),
79 ::accessibility::AccessibleStaticTextBase(CreateEditSource(pViewShell
, rCellAddress
, eSplitPos
)),
80 mpViewShell(pViewShell
),
85 pViewShell
->AddAccessibilityObject(*this);
88 ScAccessibleCell::~ScAccessibleCell()
90 if (!ScAccessibleContextBase::IsDefunc() && !rBHelper
.bInDispose
)
92 // increment refcount to prevent double call off dtor
93 osl_atomic_increment( &m_refCount
);
94 // call dispose to inform object which have a weak reference to this object
99 void ScAccessibleCell::Init()
101 ScAccessibleCellBase::Init();
103 SetEventSource(this);
106 void SAL_CALL
ScAccessibleCell::disposing()
108 SolarMutexGuard aGuard
;
109 // dispose in AccessibleStaticTextBase
114 mpViewShell
->RemoveAccessibilityObject(*this);
119 ScAccessibleCellBase::disposing();
122 //===== XInterface =====================================================
124 IMPLEMENT_FORWARD_XINTERFACE3( ScAccessibleCell
, ScAccessibleCellBase
, AccessibleStaticTextBase
, ScAccessibleCellAttributeImpl
)
126 //===== XTypeProvider ===================================================
128 IMPLEMENT_FORWARD_XTYPEPROVIDER3( ScAccessibleCell
, ScAccessibleCellBase
, AccessibleStaticTextBase
, ScAccessibleCellAttributeImpl
)
130 //===== XAccessibleComponent ============================================
132 uno::Reference
< XAccessible
> SAL_CALL
ScAccessibleCell::getAccessibleAtPoint(
133 const awt::Point
& rPoint
)
134 throw (uno::RuntimeException
, std::exception
)
136 return AccessibleStaticTextBase::getAccessibleAtPoint(rPoint
);
139 void SAL_CALL
ScAccessibleCell::grabFocus( )
140 throw (uno::RuntimeException
, std::exception
)
142 SolarMutexGuard aGuard
;
144 if (getAccessibleParent().is() && mpViewShell
)
146 uno::Reference
<XAccessibleComponent
> xAccessibleComponent(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY
);
147 if (xAccessibleComponent
.is())
149 xAccessibleComponent
->grabFocus();
150 mpViewShell
->SetCursor(maCellAddress
.Col(), maCellAddress
.Row());
155 Rectangle
ScAccessibleCell::GetBoundingBoxOnScreen() const
156 throw (uno::RuntimeException
, std::exception
)
158 Rectangle
aCellRect(GetBoundingBox());
161 vcl::Window
* pWindow
= mpViewShell
->GetWindowByPos(meSplitPos
);
164 Rectangle aRect
= pWindow
->GetWindowExtentsRelative(NULL
);
165 aCellRect
.setX(aCellRect
.getX() + aRect
.getX());
166 aCellRect
.setY(aCellRect
.getY() + aRect
.getY());
172 Rectangle
ScAccessibleCell::GetBoundingBox() const
173 throw (uno::RuntimeException
, std::exception
)
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
, true));
184 vcl::Window
* pWindow
= mpViewShell
->GetWindowByPos(meSplitPos
);
187 Rectangle
aRect(pWindow
->GetWindowExtentsRelative(pWindow
->GetAccessibleParentWindow()));
188 aRect
.Move(-aRect
.Left(), -aRect
.Top());
189 aCellRect
= aRect
.Intersection(aCellRect
);
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. */
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) )
203 Rectangle aParaRect
= GetParagraphBoundingBox();
204 if( !aParaRect
.IsEmpty() && (aCellRect
.GetWidth() < aParaRect
.GetWidth()) )
205 aCellRect
.SetSize( Size( aParaRect
.GetWidth(), aCellRect
.GetHeight() ) );
209 if (aCellRect
.IsEmpty())
210 aCellRect
.SetPos(Point(-1, -1));
214 //===== XAccessibleContext ==============================================
217 ScAccessibleCell::getAccessibleChildCount()
218 throw (uno::RuntimeException
, std::exception
)
220 return AccessibleStaticTextBase::getAccessibleChildCount();
223 uno::Reference
< XAccessible
> SAL_CALL
224 ScAccessibleCell::getAccessibleChild(sal_Int32 nIndex
)
225 throw (uno::RuntimeException
,
226 lang::IndexOutOfBoundsException
, std::exception
)
228 return AccessibleStaticTextBase::getAccessibleChild(nIndex
);
231 uno::Reference
<XAccessibleStateSet
> SAL_CALL
232 ScAccessibleCell::getAccessibleStateSet()
233 throw (uno::RuntimeException
, std::exception
)
235 SolarMutexGuard aGuard
;
236 uno::Reference
<XAccessibleStateSet
> xParentStates
;
237 if (getAccessibleParent().is())
239 uno::Reference
<XAccessibleContext
> xParentContext
= getAccessibleParent()->getAccessibleContext();
240 xParentStates
= xParentContext
->getAccessibleStateSet();
242 utl::AccessibleStateSetHelper
* pStateSet
= new utl::AccessibleStateSetHelper();
243 if (IsDefunc(xParentStates
))
244 pStateSet
->AddState(AccessibleStateType::DEFUNC
);
249 pStateSet
->AddState(AccessibleStateType::ENABLED
);
250 pStateSet
->AddState(AccessibleStateType::MULTI_LINE
);
251 pStateSet
->AddState(AccessibleStateType::MULTI_SELECTABLE
);
252 if (IsOpaque(xParentStates
))
253 pStateSet
->AddState(AccessibleStateType::OPAQUE
);
254 pStateSet
->AddState(AccessibleStateType::SELECTABLE
);
256 pStateSet
->AddState(AccessibleStateType::SELECTED
);
258 pStateSet
->AddState(AccessibleStateType::SHOWING
);
259 pStateSet
->AddState(AccessibleStateType::TRANSIENT
);
261 pStateSet
->AddState(AccessibleStateType::VISIBLE
);
264 if (IsEditable(xParentStates
))
266 pStateSet
->AddState(AccessibleStateType::EDITABLE
);
267 pStateSet
->AddState(AccessibleStateType::RESIZABLE
);
269 pStateSet
->AddState(AccessibleStateType::ENABLED
);
270 pStateSet
->AddState(AccessibleStateType::MULTI_LINE
);
271 pStateSet
->AddState(AccessibleStateType::MULTI_SELECTABLE
);
272 pStateSet
->AddState(AccessibleStateType::FOCUSABLE
);
273 if (IsOpaque(xParentStates
))
274 pStateSet
->AddState(AccessibleStateType::OPAQUE
);
275 pStateSet
->AddState(AccessibleStateType::SELECTABLE
);
277 pStateSet
->AddState(AccessibleStateType::SELECTED
);
279 pStateSet
->AddState(AccessibleStateType::SHOWING
);
280 pStateSet
->AddState(AccessibleStateType::TRANSIENT
);
282 pStateSet
->AddState(AccessibleStateType::VISIBLE
);
287 uno::Reference
<XAccessibleRelationSet
> SAL_CALL
288 ScAccessibleCell::getAccessibleRelationSet()
289 throw (uno::RuntimeException
, std::exception
)
291 SolarMutexGuard aGuard
;
293 utl::AccessibleRelationSetHelper
* pRelationSet
= NULL
;
295 pRelationSet
= mpAccDoc
->GetRelationSet(&maCellAddress
);
297 pRelationSet
= new utl::AccessibleRelationSetHelper();
298 FillDependends(pRelationSet
);
299 FillPrecedents(pRelationSet
);
303 //===== XServiceInfo ====================================================
305 OUString SAL_CALL
ScAccessibleCell::getImplementationName()
306 throw (uno::RuntimeException
, std::exception
)
308 return OUString("ScAccessibleCell");
311 uno::Sequence
< OUString
> SAL_CALL
312 ScAccessibleCell::getSupportedServiceNames()
313 throw (uno::RuntimeException
, std::exception
)
315 uno::Sequence
< OUString
> aSequence
= ScAccessibleContextBase::getSupportedServiceNames();
316 sal_Int32
nOldSize(aSequence
.getLength());
317 aSequence
.realloc(nOldSize
+ 1);
319 aSequence
[nOldSize
] = "com.sun.star.sheet.AccessibleCell";
324 //==== internal =========================================================
326 bool ScAccessibleCell::IsDefunc(
327 const uno::Reference
<XAccessibleStateSet
>& rxParentStates
)
329 return ScAccessibleContextBase::IsDefunc() || (mpDoc
== NULL
) || (mpViewShell
== NULL
) || !getAccessibleParent().is() ||
330 (rxParentStates
.is() && rxParentStates
->contains(AccessibleStateType::DEFUNC
));
333 bool ScAccessibleCell::IsEditable(
334 const uno::Reference
<XAccessibleStateSet
>& rxParentStates
)
336 bool bEditable(true);
337 if (rxParentStates
.is() && !rxParentStates
->contains(AccessibleStateType::EDITABLE
) &&
340 // here I have to test whether the protection of the table should influence this cell.
341 const ScProtectionAttr
* pItem
= static_cast<const ScProtectionAttr
*>(mpDoc
->GetAttr(
342 maCellAddress
.Col(), maCellAddress
.Row(),
343 maCellAddress
.Tab(), ATTR_PROTECTION
));
345 bEditable
= !pItem
->GetProtection();
350 bool ScAccessibleCell::IsOpaque(
351 const uno::Reference
<XAccessibleStateSet
>& /* rxParentStates */)
353 // test whether there is a background color
357 const SvxBrushItem
* pItem
= static_cast<const SvxBrushItem
*>(mpDoc
->GetAttr(
358 maCellAddress
.Col(), maCellAddress
.Row(),
359 maCellAddress
.Tab(), ATTR_BACKGROUND
));
361 bOpaque
= pItem
->GetColor() != COL_TRANSPARENT
;
366 bool ScAccessibleCell::IsSelected()
370 const ScAccessibleSpreadsheet
*pSheet
=static_cast<const ScAccessibleSpreadsheet
*>(mxParent
.get());
373 return pSheet
->IsScAddrFormulaSel(maCellAddress
);
381 const ScMarkData
& rMarkdata
= mpViewShell
->GetViewData().GetMarkData();
382 bResult
= rMarkdata
.IsCellMarked(maCellAddress
.Col(), maCellAddress
.Row());
387 ScDocument
* ScAccessibleCell::GetDocument(ScTabViewShell
* pViewShell
)
389 ScDocument
* pDoc
= NULL
;
391 pDoc
= pViewShell
->GetViewData().GetDocument();
395 ::std::unique_ptr
< SvxEditSource
> ScAccessibleCell::CreateEditSource(ScTabViewShell
* pViewShell
, ScAddress aCell
, ScSplitPos eSplitPos
)
399 return ::std::unique_ptr
< SvxEditSource
>();
401 ::std::unique_ptr
< ScAccessibleTextData
> pAccessibleCellTextData
402 ( new ScAccessibleCellTextData( pViewShell
, aCell
, eSplitPos
, this ) );
403 ::std::unique_ptr
< SvxEditSource
> pEditSource (new ScAccessibilityEditSource(std::move(pAccessibleCellTextData
)));
408 void ScAccessibleCell::FillDependends(utl::AccessibleRelationSetHelper
* pRelationSet
)
412 ScRange
aRange(0, 0, maCellAddress
.Tab(), MAXCOL
, MAXROW
, maCellAddress
.Tab());
413 ScCellIterator
aCellIter(mpDoc
, aRange
);
415 for (bool bHasCell
= aCellIter
.first(); bHasCell
; bHasCell
= aCellIter
.next())
417 if (aCellIter
.getType() == CELLTYPE_FORMULA
)
420 ScDetectiveRefIter
aIter(aCellIter
.getFormulaCell());
422 while ( !bFound
&& aIter
.GetNextRef( aRef
) )
424 if (aRef
.In(maCellAddress
))
428 AddRelation(aCellIter
.GetPos(), AccessibleRelationType::CONTROLLER_FOR
, pRelationSet
);
434 void ScAccessibleCell::FillPrecedents(utl::AccessibleRelationSetHelper
* pRelationSet
)
436 if (mpDoc
&& mpDoc
->GetCellType(maCellAddress
) == CELLTYPE_FORMULA
)
438 ScFormulaCell
* pCell
= mpDoc
->GetFormulaCell(maCellAddress
);
441 ScDetectiveRefIter
aIter(pCell
);
443 while ( aIter
.GetNextRef( aRef
) )
445 AddRelation( aRef
, AccessibleRelationType::CONTROLLED_BY
, pRelationSet
);
450 void ScAccessibleCell::AddRelation(const ScAddress
& rCell
,
451 const sal_uInt16 aRelationType
,
452 utl::AccessibleRelationSetHelper
* pRelationSet
)
454 AddRelation(ScRange(rCell
, rCell
), aRelationType
, pRelationSet
);
457 void ScAccessibleCell::AddRelation(const ScRange
& rRange
,
458 const sal_uInt16 aRelationType
,
459 utl::AccessibleRelationSetHelper
* pRelationSet
)
461 uno::Reference
< XAccessibleTable
> xTable ( getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY
);
464 sal_uInt32
nCount(static_cast<sal_uInt32
>(rRange
.aEnd
.Col() -
465 rRange
.aStart
.Col() + 1) * (rRange
.aEnd
.Row() -
466 rRange
.aStart
.Row() + 1));
467 uno::Sequence
< uno::Reference
< uno::XInterface
> > aTargetSet( nCount
);
468 uno::Reference
< uno::XInterface
>* pTargetSet
= aTargetSet
.getArray();
472 for (sal_uInt32 nRow
= rRange
.aStart
.Row(); nRow
<= sal::static_int_cast
<sal_uInt32
>(rRange
.aEnd
.Row()); ++nRow
)
474 for (sal_uInt32 nCol
= rRange
.aStart
.Col(); nCol
<= sal::static_int_cast
<sal_uInt32
>(rRange
.aEnd
.Col()); ++nCol
)
476 pTargetSet
[nPos
] = xTable
->getAccessibleCellAt(nRow
, nCol
);
480 OSL_ENSURE(nCount
== nPos
, "something wents wrong");
482 AccessibleRelation aRelation
;
483 aRelation
.RelationType
= aRelationType
;
484 aRelation
.TargetSet
= aTargetSet
;
485 pRelationSet
->AddRelation(aRelation
);
489 static OUString
ReplaceOneChar(const OUString
& oldOUString
, const OUString
& replacedChar
, const OUString
& replaceStr
)
491 int iReplace
= oldOUString
.lastIndexOf(replacedChar
);
492 OUString aRet
= oldOUString
;
495 aRet
= aRet
.replaceAt(iReplace
, 1, replaceStr
);
496 iReplace
= aRet
.lastIndexOf(replacedChar
, iReplace
);
501 static OUString
ReplaceFourChar(const OUString
& oldOUString
)
503 OUString aRet
= ReplaceOneChar(oldOUString
, "\\", "\\\\");
504 aRet
= ReplaceOneChar(aRet
, ";", "\\;");
505 aRet
= ReplaceOneChar(aRet
, "=", "\\=");
506 aRet
= ReplaceOneChar(aRet
, ",", "\\,");
507 aRet
= ReplaceOneChar(aRet
, ":", "\\:");
511 uno::Any SAL_CALL
ScAccessibleCell::getExtendedAttributes()
512 throw (::com::sun::star::lang::IndexOutOfBoundsException
,
513 ::com::sun::star::uno::RuntimeException
,
516 SolarMutexGuard aGuard
;
521 OUString strFor
= mpViewShell
->GetFormula(maCellAddress
) ;
522 strFor
= strFor
.replaceAt(0,1,"");
523 strFor
= ReplaceFourChar(strFor
);
524 strFor
= "Formula:" + strFor
;
526 strFor
+= ReplaceFourChar(GetAllDisplayNote());
528 strFor
+= getShadowAttrs();//the string returned contains the spliter ";"
529 strFor
+= getBorderAttrs();//the string returned contains the spliter ";"
530 //end of cell attributes
533 strFor
+= "isdropdown:";
545 // cell has its own ParaIndent property, so when calling character attributes on cell, the ParaIndent should replace the ParaLeftMargin if its value is not zero.
546 uno::Sequence
< beans::PropertyValue
> SAL_CALL
ScAccessibleCell::getCharacterAttributes( sal_Int32 nIndex
, const ::com::sun::star::uno::Sequence
< OUString
>& aRequestedAttributes
) throw (lang::IndexOutOfBoundsException
, uno::RuntimeException
, std::exception
)
548 SolarMutexGuard aGuard
;
550 uno::Sequence
< beans::PropertyValue
> aAttribs
= AccessibleStaticTextBase::getCharacterAttributes( nIndex
, aRequestedAttributes
);
551 beans::PropertyValue
*pAttribs
= aAttribs
.getArray();
553 sal_uInt16 nParaIndent
= static_cast< const SfxUInt16Item
* >( mpDoc
->GetAttr( maCellAddress
.Col(), maCellAddress
.Row(), maCellAddress
.Tab(), ATTR_INDENT
) )->GetValue();
556 OUString
sLeftMarginName ("ParaLeftMargin");
557 for (int i
= 0; i
< aAttribs
.getLength(); ++i
)
559 if (sLeftMarginName
== pAttribs
[i
].Name
)
561 pAttribs
[i
].Value
= uno::makeAny( nParaIndent
);
569 bool ScAccessibleCell::IsFormulaMode()
571 ScAccessibleSpreadsheet
* pSheet
= static_cast<ScAccessibleSpreadsheet
*>(mxParent
.get());
574 return pSheet
->IsFormulaMode();
579 bool ScAccessibleCell::IsDropdown()
581 sal_uInt16 nPosX
= maCellAddress
.Col();
582 sal_uInt16 nPosY
= sal_uInt16(maCellAddress
.Row());
583 sal_uInt16 nTab
= maCellAddress
.Tab();
584 sal_uInt32 nValidation
= static_cast< const SfxUInt32Item
* >( mpDoc
->GetAttr( nPosX
, nPosY
, nTab
, ATTR_VALIDDATA
) )->GetValue();
587 const ScValidationData
* pData
= mpDoc
->GetValidationEntry( nValidation
);
588 if( pData
&& pData
->HasSelectionList() )
591 const ScMergeFlagAttr
* pAttr
;
592 pAttr
= static_cast<const ScMergeFlagAttr
*>(mpDoc
->GetAttr( nPosX
, nPosY
, nTab
, ATTR_MERGE_FLAG
));
593 if( pAttr
->HasAutoFilter() )
599 sal_uInt16 nTabCount
= mpDoc
->GetTableCount();
600 if ( nTab
+1<nTabCount
&& mpDoc
->IsScenario(nTab
+1) && !mpDoc
->IsScenario(nTab
) )
604 for (i
=nTab
+1; i
<nTabCount
&& mpDoc
->IsScenario(i
); i
++)
605 mpDoc
->MarkScenario( i
, nTab
, aMarks
, false, SC_SCENARIO_SHOWFRAME
);
607 aMarks
.FillRangeListWithMarks( &aRanges
, false );
609 SCTAB nRangeCount
= aRanges
.size();
610 for (i
=0; i
<nRangeCount
; i
++)
612 ScRange aRange
= *aRanges
[i
];
613 mpDoc
->ExtendTotalMerge( aRange
);
614 bool bTextBelow
= ( aRange
.aStart
.Row() == 0 );
615 // MT IA2: Not used: sal_Bool bIsInScen = sal_False;
618 bHasScenario
= (aRange
.aStart
.Col() == nPosX
&& aRange
.aEnd
.Row() == nPosY
-1);
622 bHasScenario
= (aRange
.aStart
.Col() == nPosX
&& aRange
.aStart
.Row() == nPosY
+1);
624 if( bHasScenario
) return true;
631 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */