tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / ui / Accessibility / AccessibleCell.cxx
blobdd7f189738b946c5f1be6eccce1e212cf8c497b7
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <memory>
21 #include <string_view>
23 #include <sal/config.h>
25 #include <AccessibleCell.hxx>
26 #include <scitems.hxx>
27 #include <AccessibleText.hxx>
28 #include <AccessibleDocument.hxx>
29 #include <tabvwsh.hxx>
30 #include <comphelper/sequence.hxx>
31 #include <document.hxx>
32 #include <attrib.hxx>
33 #include <editsrc.hxx>
34 #include <dociter.hxx>
35 #include <markdata.hxx>
36 #include <cellvalue.hxx>
37 #include <formulaiter.hxx>
38 #include <validat.hxx>
40 #include <unotools/accessiblerelationsethelper.hxx>
41 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
42 #include <com/sun/star/accessibility/AccessibleRelationType.hpp>
43 #include <com/sun/star/accessibility/XAccessibleTable.hpp>
44 #include <editeng/brushitem.hxx>
45 #include <vcl/svapp.hxx>
47 #include <AccessibleSpreadsheet.hxx>
49 using namespace ::com::sun::star;
50 using namespace ::com::sun::star::accessibility;
52 rtl::Reference<ScAccessibleCell> ScAccessibleCell::create(
53 const uno::Reference<XAccessible>& rxParent,
54 ScTabViewShell* pViewShell,
55 const ScAddress& rCellAddress,
56 sal_Int64 nIndex,
57 ScSplitPos eSplitPos,
58 ScAccessibleDocument* pAccDoc)
60 rtl::Reference<ScAccessibleCell> x(new ScAccessibleCell(
61 rxParent, pViewShell, rCellAddress, nIndex, eSplitPos, pAccDoc));
62 x->Init();
63 return x;
66 ScAccessibleCell::ScAccessibleCell(
67 const uno::Reference<XAccessible>& rxParent,
68 ScTabViewShell* pViewShell,
69 const ScAddress& rCellAddress,
70 sal_Int64 nIndex,
71 ScSplitPos eSplitPos,
72 ScAccessibleDocument* pAccDoc)
74 ScAccessibleCellBase(rxParent, GetDocument(pViewShell), rCellAddress, nIndex),
75 ::accessibility::AccessibleStaticTextBase(CreateEditSource(pViewShell, rCellAddress, eSplitPos)),
76 mpViewShell(pViewShell),
77 mpAccDoc(pAccDoc),
78 meSplitPos(eSplitPos)
80 if (pViewShell)
81 pViewShell->AddAccessibilityObject(*this);
84 ScAccessibleCell::~ScAccessibleCell()
86 if (!ScAccessibleContextBase::IsDefunc() && !rBHelper.bInDispose)
88 // increment refcount to prevent double call off dtor
89 osl_atomic_increment( &m_refCount );
90 // call dispose to inform object which have a weak reference to this object
91 dispose();
95 void ScAccessibleCell::Init()
97 ScAccessibleCellBase::Init();
99 SetEventSource(this);
102 void SAL_CALL ScAccessibleCell::disposing()
104 SolarMutexGuard aGuard;
105 // dispose in AccessibleStaticTextBase
106 Dispose();
108 if (mpViewShell)
110 mpViewShell->RemoveAccessibilityObject(*this);
111 mpViewShell = nullptr;
113 mpAccDoc = nullptr;
115 ScAccessibleCellBase::disposing();
118 //===== XInterface =====================================================
120 IMPLEMENT_FORWARD_XINTERFACE3( ScAccessibleCell, ScAccessibleCellBase, AccessibleStaticTextBase, ScAccessibleCellAttributeImpl )
122 //===== XTypeProvider ===================================================
124 css::uno::Sequence< css::uno::Type > SAL_CALL ScAccessibleCell::getTypes()
126 return ::comphelper::concatSequences(
127 ScAccessibleCellBase::getTypes(),
128 AccessibleStaticTextBase::getTypes(),
129 ScAccessibleCellAttributeImpl::getTypes()
132 IMPLEMENT_GET_IMPLEMENTATION_ID( ScAccessibleCell )
134 //===== XAccessibleComponent ============================================
136 uno::Reference< XAccessible > SAL_CALL ScAccessibleCell::getAccessibleAtPoint(
137 const awt::Point& rPoint )
139 return AccessibleStaticTextBase::getAccessibleAtPoint(rPoint);
142 void SAL_CALL ScAccessibleCell::grabFocus( )
144 SolarMutexGuard aGuard;
145 IsObjectValid();
146 if (getAccessibleParent().is() && mpViewShell)
148 uno::Reference<XAccessibleComponent> xAccessibleComponent(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY);
149 if (xAccessibleComponent.is())
151 xAccessibleComponent->grabFocus();
152 mpViewShell->SetCursor(maCellAddress.Col(), maCellAddress.Row());
157 AbsoluteScreenPixelRectangle ScAccessibleCell::GetBoundingBoxOnScreen() const
159 AbsoluteScreenPixelRectangle aCellRect(GetBoundingBox());
160 if (mpViewShell)
162 vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
163 if (pWindow)
165 AbsoluteScreenPixelRectangle aRect = pWindow->GetWindowExtentsAbsolute();
166 aCellRect.Move(aRect.Left(), aRect.Top());
169 return aCellRect;
172 tools::Rectangle ScAccessibleCell::GetBoundingBox() const
174 tools::Rectangle aCellRect;
175 if (mpViewShell)
177 tools::Long nSizeX, nSizeY;
178 mpViewShell->GetViewData().GetMergeSizePixel(
179 maCellAddress.Col(), maCellAddress.Row(), nSizeX, nSizeY);
180 aCellRect.SetSize(Size(nSizeX, nSizeY));
181 aCellRect.SetPos(mpViewShell->GetViewData().GetScrPos(maCellAddress.Col(), maCellAddress.Row(), meSplitPos, true));
183 vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
184 if (pWindow)
186 tools::Rectangle aRect(pWindow->GetWindowExtentsRelative(*pWindow->GetAccessibleParentWindow()));
187 aRect.Move(-aRect.Left(), -aRect.Top());
188 aCellRect = aRect.Intersection(aCellRect);
191 /* #i19430# Gnopernicus reads text partly if it sticks out of the cell
192 boundaries. This leads to wrong results in cases where the cell
193 text is rotated, because rotation is not taken into account when
194 calculating the visible part of the text. In these cases we will
195 simply expand the cell size to the width of the unrotated text. */
196 if (mpDoc)
198 const ScRotateValueItem* pItem = mpDoc->GetAttr( maCellAddress, ATTR_ROTATE_VALUE );
199 if( pItem && (pItem->GetValue() != 0_deg100) )
201 tools::Rectangle aParaRect = GetParagraphBoundingBox();
202 if( !aParaRect.IsEmpty() && (aCellRect.GetWidth() < aParaRect.GetWidth()) )
203 aCellRect.SetSize( Size( aParaRect.GetWidth(), aCellRect.GetHeight() ) );
207 if (aCellRect.IsEmpty())
208 aCellRect.SetPos(Point(-1, -1));
209 return aCellRect;
212 //===== XAccessibleContext ==============================================
214 sal_Int64 SAL_CALL
215 ScAccessibleCell::getAccessibleChildCount()
217 return AccessibleStaticTextBase::getAccessibleChildCount();
220 uno::Reference< XAccessible > SAL_CALL
221 ScAccessibleCell::getAccessibleChild(sal_Int64 nIndex)
223 return AccessibleStaticTextBase::getAccessibleChild(nIndex);
226 sal_Int64 SAL_CALL
227 ScAccessibleCell::getAccessibleStateSet()
229 SolarMutexGuard aGuard;
230 sal_Int64 nParentStates = 0;
231 if (getAccessibleParent().is())
233 uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
234 nParentStates = xParentContext->getAccessibleStateSet();
236 sal_Int64 nStateSet = 0;
237 if (IsDefunc(nParentStates))
238 nStateSet |= AccessibleStateType::DEFUNC;
239 else
241 if (IsFocused())
242 nStateSet |= AccessibleStateType::FOCUSED;
244 if (IsFormulaMode())
246 nStateSet |= AccessibleStateType::ENABLED;
247 nStateSet |= AccessibleStateType::MULTI_LINE;
248 nStateSet |= AccessibleStateType::MULTI_SELECTABLE;
249 if (IsOpaque())
250 nStateSet |= AccessibleStateType::OPAQUE;
251 nStateSet |= AccessibleStateType::SELECTABLE;
252 if (IsSelected())
253 nStateSet |= AccessibleStateType::SELECTED;
254 if (isShowing())
255 nStateSet |= AccessibleStateType::SHOWING;
256 nStateSet |= AccessibleStateType::TRANSIENT;
257 if (isVisible())
258 nStateSet |= AccessibleStateType::VISIBLE;
259 return nStateSet;
261 if (IsEditable(nParentStates))
263 nStateSet |= AccessibleStateType::EDITABLE;
264 nStateSet |= AccessibleStateType::RESIZABLE;
266 nStateSet |= AccessibleStateType::ENABLED;
267 nStateSet |= AccessibleStateType::MULTI_LINE;
268 nStateSet |= AccessibleStateType::MULTI_SELECTABLE;
269 nStateSet |= AccessibleStateType::FOCUSABLE;
270 if (IsOpaque())
271 nStateSet |= AccessibleStateType::OPAQUE;
272 nStateSet |= AccessibleStateType::SELECTABLE;
273 if (IsSelected())
274 nStateSet |= AccessibleStateType::SELECTED;
275 if (isShowing())
276 nStateSet |= AccessibleStateType::SHOWING;
277 nStateSet |= AccessibleStateType::TRANSIENT;
278 if (isVisible())
279 nStateSet |= AccessibleStateType::VISIBLE;
281 return nStateSet;
284 uno::Reference<XAccessibleRelationSet> SAL_CALL
285 ScAccessibleCell::getAccessibleRelationSet()
287 SolarMutexGuard aGuard;
288 IsObjectValid();
289 rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSet;
290 if (mpAccDoc)
291 pRelationSet = mpAccDoc->GetRelationSet(&maCellAddress);
292 if (!pRelationSet)
293 pRelationSet = new utl::AccessibleRelationSetHelper();
294 FillDependents(pRelationSet.get());
295 FillPrecedents(pRelationSet.get());
296 return pRelationSet;
299 //===== XServiceInfo ====================================================
301 OUString SAL_CALL ScAccessibleCell::getImplementationName()
303 return u"ScAccessibleCell"_ustr;
306 uno::Sequence< OUString> SAL_CALL
307 ScAccessibleCell::getSupportedServiceNames()
309 const css::uno::Sequence<OUString> vals { u"com.sun.star.sheet.AccessibleCell"_ustr };
310 return comphelper::concatSequences(ScAccessibleContextBase::getSupportedServiceNames(), vals);
313 //==== internal =========================================================
315 bool ScAccessibleCell::IsDefunc(sal_Int64 nParentStates)
317 return ScAccessibleContextBase::IsDefunc() || (mpDoc == nullptr) || (mpViewShell == nullptr) || !getAccessibleParent().is() ||
318 (nParentStates & AccessibleStateType::DEFUNC);
321 bool ScAccessibleCell::IsEditable(sal_Int64 nParentStates)
323 bool bEditable(true);
324 if ( !(nParentStates & AccessibleStateType::EDITABLE) &&
325 mpDoc)
327 // here I have to test whether the protection of the table should influence this cell.
328 const ScProtectionAttr* pItem = mpDoc->GetAttr(maCellAddress, ATTR_PROTECTION);
329 if (pItem)
330 bEditable = !pItem->GetProtection();
332 return bEditable;
335 bool ScAccessibleCell::IsOpaque() const
337 // test whether there is a background color
338 bool bOpaque(true);
339 if (mpDoc)
341 const SvxBrushItem* pItem = mpDoc->GetAttr(maCellAddress, ATTR_BACKGROUND);
342 if (pItem)
343 bOpaque = pItem->GetColor() != COL_TRANSPARENT;
345 return bOpaque;
348 bool ScAccessibleCell::IsFocused() const
350 if (mpViewShell && mpViewShell->GetViewData().GetCurPos() == maCellAddress)
351 return mpViewShell->GetActiveWin()->HasFocus();
353 return false;
356 bool ScAccessibleCell::IsSelected()
358 if (IsFormulaMode())
360 const ScAccessibleSpreadsheet *pSheet =static_cast<const ScAccessibleSpreadsheet*>(mxParent.get());
361 if (pSheet)
363 return pSheet->IsScAddrFormulaSel(maCellAddress);
365 return false;
368 bool bResult(false);
369 if (mpViewShell)
371 const ScMarkData& rMarkdata = mpViewShell->GetViewData().GetMarkData();
372 bResult = rMarkdata.IsCellMarked(maCellAddress.Col(), maCellAddress.Row());
374 return bResult;
377 ScDocument* ScAccessibleCell::GetDocument(ScTabViewShell* pViewShell)
379 ScDocument* pDoc = nullptr;
380 if (pViewShell)
381 pDoc = &pViewShell->GetViewData().GetDocument();
382 return pDoc;
385 ::std::unique_ptr< SvxEditSource > ScAccessibleCell::CreateEditSource(ScTabViewShell* pViewShell, ScAddress aCell, ScSplitPos eSplitPos)
387 if (IsFormulaMode())
389 return ::std::unique_ptr< SvxEditSource >();
391 ::std::unique_ptr< SvxEditSource > pEditSource (new ScAccessibilityEditSource(std::make_unique<ScAccessibleCellTextData>(pViewShell, aCell, eSplitPos, this)));
393 return pEditSource;
396 void ScAccessibleCell::FillDependents(utl::AccessibleRelationSetHelper* pRelationSet)
398 if (!mpDoc)
399 return;
401 ScRange aRange(0, 0, maCellAddress.Tab(), mpDoc->MaxCol(), mpDoc->MaxRow(), maCellAddress.Tab());
402 ScCellIterator aCellIter(*mpDoc, aRange);
404 for (bool bHasCell = aCellIter.first(); bHasCell; bHasCell = aCellIter.next())
406 if (aCellIter.getType() == CELLTYPE_FORMULA)
408 bool bFound = false;
409 ScDetectiveRefIter aIter(*mpDoc, aCellIter.getFormulaCell());
410 ScRange aRef;
411 while ( !bFound && aIter.GetNextRef( aRef ) )
413 if (aRef.Contains(maCellAddress))
414 bFound = true;
416 if (bFound)
417 AddRelation(aCellIter.GetPos(), AccessibleRelationType_CONTROLLER_FOR, pRelationSet);
422 void ScAccessibleCell::FillPrecedents(utl::AccessibleRelationSetHelper* pRelationSet)
424 if (!mpDoc)
425 return;
427 ScRefCellValue aCell(*mpDoc, maCellAddress);
428 if (aCell.getType() == CELLTYPE_FORMULA)
430 ScFormulaCell* pCell = aCell.getFormula();
431 ScDetectiveRefIter aIter(*mpDoc, pCell);
432 ScRange aRef;
433 while ( aIter.GetNextRef( aRef ) )
435 AddRelation( aRef, AccessibleRelationType_CONTROLLED_BY, pRelationSet);
440 void ScAccessibleCell::AddRelation(const ScAddress& rCell,
441 const AccessibleRelationType eRelationType,
442 utl::AccessibleRelationSetHelper* pRelationSet)
444 AddRelation(ScRange(rCell, rCell), eRelationType, pRelationSet);
447 void ScAccessibleCell::AddRelation(const ScRange& rRange,
448 const AccessibleRelationType eRelationType,
449 utl::AccessibleRelationSetHelper* pRelationSet)
451 uno::Reference < XAccessibleTable > xTable ( getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY );
452 if (!xTable.is())
453 return;
455 const sal_uInt32 nCount(static_cast<sal_uInt32>(rRange.aEnd.Col() -
456 rRange.aStart.Col() + 1) * (rRange.aEnd.Row() -
457 rRange.aStart.Row() + 1));
459 // tdf#157299 avoid handling a large amount of cells for performance reasons
460 if (nCount > 1000)
462 SAL_WARN("sc", "ScAccessibleCell::AddRelation: Not setting relations "
463 "for cell range with more than 1000 cells for performance reasons.");
464 return;
467 uno::Sequence<uno::Reference<css::accessibility::XAccessible>> aTargetSet(nCount);
468 uno::Reference <css::accessibility::XAccessible>* pTargetSet = aTargetSet.getArray();
469 sal_uInt32 nPos(0);
470 for (sal_uInt32 nRow = rRange.aStart.Row(); nRow <= sal::static_int_cast<sal_uInt32>(rRange.aEnd.Row()); ++nRow)
472 for (sal_uInt32 nCol = rRange.aStart.Col(); nCol <= sal::static_int_cast<sal_uInt32>(rRange.aEnd.Col()); ++nCol)
474 pTargetSet[nPos] = xTable->getAccessibleCellAt(nRow, nCol);
475 ++nPos;
478 OSL_ENSURE(nCount == nPos, "something went wrong");
479 AccessibleRelation aRelation;
480 aRelation.RelationType = eRelationType;
481 aRelation.TargetSet = std::move(aTargetSet);
482 pRelationSet->AddRelation(aRelation);
485 static OUString ReplaceFourChar(const OUString& oldOUString)
487 return oldOUString.replaceAll(u"\\", u"\\\\")
488 .replaceAll(u";", u"\\;")
489 .replaceAll(u"=", u"\\=")
490 .replaceAll(u",", u"\\,")
491 .replaceAll(u":", u"\\:");
494 uno::Any SAL_CALL ScAccessibleCell::getExtendedAttributes()
496 SolarMutexGuard aGuard;
498 // report row and column index text via attributes as specified in ARIA which map
499 // to attributes of the same name for AT-SPI2, IAccessible2, UIA
500 // https://www.w3.org/TR/core-aam-1.2/#ariaRowIndexText
501 // https://www.w3.org/TR/core-aam-1.2/#ariaColIndexText
502 const OUString sRowIndexText = maCellAddress.Format(ScRefFlags::ROW_VALID);
503 const OUString sColIndexText = maCellAddress.Format(ScRefFlags::COL_VALID);
504 OUString sAttributes = "rowindextext:" + sRowIndexText + ";colindextext:" + sColIndexText + ";";
506 if (mpViewShell)
508 OUString strFor = mpViewShell->GetFormula(maCellAddress) ;
509 if (!strFor.isEmpty())
511 strFor = strFor.copy(1);
512 strFor = ReplaceFourChar(strFor);
514 strFor = "Formula:" + strFor +
515 ";Note:" +
516 ReplaceFourChar(GetAllDisplayNote()) + ";" +
517 getShadowAttrs() + //the string returned contains the spliter ";"
518 getBorderAttrs();//the string returned contains the spliter ";"
519 //end of cell attributes
520 if( mpDoc )
522 strFor += "isdropdown:";
523 if( IsDropdown() )
524 strFor += "true";
525 else
526 strFor += "false";
527 strFor += ";";
529 sAttributes += strFor ;
532 return uno::Any(sAttributes);
535 // 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.
536 uno::Sequence< beans::PropertyValue > SAL_CALL ScAccessibleCell::getCharacterAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& aRequestedAttributes )
538 SolarMutexGuard aGuard;
540 uno::Sequence< beans::PropertyValue > aAttribs = AccessibleStaticTextBase::getCharacterAttributes( nIndex, aRequestedAttributes );
542 sal_uInt16 nParaIndent = mpDoc->GetAttr( maCellAddress, ATTR_INDENT )->GetValue();
543 if (nParaIndent > 0)
545 auto [begin, end] = asNonConstRange(aAttribs);
546 auto pAttrib = std::find_if(begin, end,
547 [](const beans::PropertyValue& rAttrib) { return "ParaLeftMargin" == rAttrib.Name; });
548 if (pAttrib != end)
549 pAttrib->Value <<= nParaIndent;
551 return aAttribs;
554 bool ScAccessibleCell::IsFormulaMode()
556 ScAccessibleSpreadsheet* pSheet = static_cast<ScAccessibleSpreadsheet*>(mxParent.get());
557 if (pSheet)
559 return pSheet->IsFormulaMode();
561 return false;
564 bool ScAccessibleCell::IsDropdown() const
566 sal_uInt16 nPosX = maCellAddress.Col();
567 sal_uInt16 nPosY = sal_uInt16(maCellAddress.Row());
568 sal_uInt16 nTab = maCellAddress.Tab();
569 sal_uInt32 nValidation = mpDoc->GetAttr( nPosX, nPosY, nTab, ATTR_VALIDDATA )->GetValue();
570 if( nValidation )
572 const ScValidationData* pData = mpDoc->GetValidationEntry( nValidation );
573 if( pData && pData->HasSelectionList() )
574 return true;
576 const ScMergeFlagAttr* pAttr = mpDoc->GetAttr( nPosX, nPosY, nTab, ATTR_MERGE_FLAG );
577 if( pAttr->HasAutoFilter() )
579 return true;
581 else
583 sal_uInt16 nTabCount = mpDoc->GetTableCount();
584 if ( nTab+1<nTabCount && mpDoc->IsScenario(nTab+1) && !mpDoc->IsScenario(nTab) )
586 SCTAB i;
587 ScMarkData aMarks(mpDoc->GetSheetLimits());
588 for (i=nTab+1; i<nTabCount && mpDoc->IsScenario(i); i++)
589 mpDoc->MarkScenario( i, nTab, aMarks, false, ScScenarioFlags::ShowFrame );
590 ScRangeList aRanges;
591 aMarks.FillRangeListWithMarks( &aRanges, false );
592 bool bHasScenario;
593 SCTAB nRangeCount = aRanges.size();
594 for (i=0; i<nRangeCount; i++)
596 ScRange aRange = aRanges[i];
597 mpDoc->ExtendTotalMerge( aRange );
598 bool bTextBelow = ( aRange.aStart.Row() == 0 );
599 // MT IA2: Not used: sal_Bool bIsInScen = sal_False;
600 if ( bTextBelow )
602 bHasScenario = (aRange.aStart.Col() == nPosX && aRange.aEnd.Row() == nPosY-1);
604 else
606 bHasScenario = (aRange.aStart.Col() == nPosX && aRange.aStart.Row() == nPosY+1);
608 if( bHasScenario ) return true;
612 return false;
615 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */