tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / ui / Accessibility / AccessibleSpreadsheet.cxx
blob0bad15e8556629ecb042af394a0dcb84b1317fce
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 <AccessibleSpreadsheet.hxx>
21 #include <AccessibleCell.hxx>
22 #include <AccessibleDocument.hxx>
23 #include <tabvwsh.hxx>
24 #include <document.hxx>
25 #include <hints.hxx>
26 #include <scmod.hxx>
27 #include <markdata.hxx>
28 #include <gridwin.hxx>
30 #include <o3tl/safeint.hxx>
31 #include <unotools/accessiblerelationsethelper.hxx>
32 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
33 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
34 #include <com/sun/star/accessibility/AccessibleTableModelChangeType.hpp>
35 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
36 #include <comphelper/accessibletexthelper.hxx>
37 #include <sal/log.hxx>
38 #include <tools/gen.hxx>
39 #include <svtools/colorcfg.hxx>
40 #include <vcl/svapp.hxx>
41 #include <scresid.hxx>
42 #include <strings.hrc>
44 #include <algorithm>
45 #include <cstdlib>
47 using namespace ::com::sun::star;
48 using namespace ::com::sun::star::accessibility;
50 static bool CompMinCol(const std::pair<sal_uInt16,sal_uInt16> & pc1,const std::pair<sal_uInt16,sal_uInt16> &pc2)
52 return pc1.first < pc2.first;
55 ScMyAddress ScAccessibleSpreadsheet::CalcScAddressFromRangeList(ScRangeList *pMarkedRanges,sal_Int32 nSelectedChildIndex)
57 if (pMarkedRanges->size() <= 1)
59 ScRange const & rRange = pMarkedRanges->front();
60 // MT IA2: Not used.
61 // const int nRowNum = rRange.aEnd.Row() - rRange.aStart.Row() + 1;
62 const int nColNum = rRange.aEnd.Col() - rRange.aStart.Col() + 1;
63 const int nCurCol = nSelectedChildIndex % nColNum;
64 const int nCurRow = (nSelectedChildIndex - nCurCol)/nColNum;
65 return ScMyAddress(static_cast<SCCOL>(rRange.aStart.Col() + nCurCol), rRange.aStart.Row() + nCurRow, maActiveCell.Tab());
67 else
69 ScDocument* pDoc= GetDocument(mpViewShell);
70 sal_Int32 nMinRow = pDoc->MaxRow();
71 sal_Int32 nMaxRow = 0;
72 std::vector<ScRange> aRanges;
73 size_t nSize = pMarkedRanges->size();
74 for (size_t i = 0; i < nSize; ++i)
76 ScRange const & rRange = (*pMarkedRanges)[i];
77 if (rRange.aStart.Tab() != rRange.aEnd.Tab())
79 if ((maActiveCell.Tab() >= rRange.aStart.Tab()) ||
80 maActiveCell.Tab() <= rRange.aEnd.Tab())
82 aRanges.push_back(rRange);
83 nMinRow = std::min(rRange.aStart.Row(),nMinRow);
84 nMaxRow = std::max(rRange.aEnd.Row(),nMaxRow);
86 else
87 SAL_WARN("sc", "Range of wrong table");
89 else if(rRange.aStart.Tab() == maActiveCell.Tab())
91 aRanges.push_back(rRange);
92 nMinRow = std::min(rRange.aStart.Row(),nMinRow);
93 nMaxRow = std::max(rRange.aEnd.Row(),nMaxRow);
95 else
96 SAL_WARN("sc", "Range of wrong table");
98 int nCurrentIndex = 0 ;
99 for(sal_Int32 row = nMinRow ; row <= nMaxRow ; ++row)
101 std::vector<std::pair<SCCOL, SCCOL>> aVecCol;
102 for (ScRange const & r : aRanges)
104 if ( row >= r.aStart.Row() && row <= r.aEnd.Row())
106 aVecCol.emplace_back(r.aStart.Col(), r.aEnd.Col());
109 std::sort(aVecCol.begin(), aVecCol.end(), CompMinCol);
110 for (const std::pair<SCCOL, SCCOL> &pairCol : aVecCol)
112 SCCOL nCol = pairCol.second - pairCol.first + 1;
113 if (nCol + nCurrentIndex > nSelectedChildIndex)
115 return ScMyAddress(static_cast<SCCOL>(pairCol.first + nSelectedChildIndex - nCurrentIndex), row, maActiveCell.Tab());
117 nCurrentIndex += nCol;
121 return ScMyAddress(0,0,maActiveCell.Tab());
124 bool ScAccessibleSpreadsheet::CalcScRangeDifferenceMax(const ScRange & rSrc, const ScRange & rDest, int nMax,
125 std::vector<ScMyAddress> &vecRet, int &nSize)
127 //Src Must be :Src > Dest
128 if (rDest.Contains(rSrc))
129 {//Here is Src In Dest,Src <= Dest
130 return false;
132 if (!rDest.Intersects(rSrc))
134 int nCellCount = sal_uInt32(rDest.aEnd.Col() - rDest.aStart.Col() + 1)
135 * sal_uInt32(rDest.aEnd.Row() - rDest.aStart.Row() + 1)
136 * sal_uInt32(rDest.aEnd.Tab() - rDest.aStart.Tab() + 1);
137 if (nCellCount + nSize > nMax)
139 return true;
141 else if(nCellCount > 0)
143 for (sal_Int32 row = rDest.aStart.Row(); row <= rDest.aEnd.Row();++row)
145 for (sal_uInt16 col = rDest.aStart.Col(); col <= rDest.aEnd.Col();++col)
147 vecRet.emplace_back(col,row,rDest.aStart.Tab());
151 return false;
153 sal_Int32 nMinRow = rSrc.aStart.Row();
154 sal_Int32 nMaxRow = rSrc.aEnd.Row();
155 for (; nMinRow <= nMaxRow ; ++nMinRow,--nMaxRow)
157 for (sal_uInt16 col = rSrc.aStart.Col(); col <= rSrc.aEnd.Col();++col)
159 if (nSize > nMax)
161 return true;
163 ScMyAddress cell(col,nMinRow,rSrc.aStart.Tab());
164 if(!rDest.Contains(cell))
165 {//In Src ,Not In Dest
166 vecRet.push_back(cell);
167 ++nSize;
170 if (nMinRow != nMaxRow)
172 for (sal_uInt16 col = rSrc.aStart.Col(); col <= rSrc.aEnd.Col();++col)
174 if (nSize > nMax)
176 return true;
178 ScMyAddress cell(col,nMaxRow,rSrc.aStart.Tab());
179 if(!rDest.Contains(cell))
180 {//In Src ,Not In Dest
181 vecRet.push_back(cell);
182 ++nSize;
187 return false;
190 //In Src , Not in Dest
191 bool ScAccessibleSpreadsheet::CalcScRangeListDifferenceMax(ScRangeList *pSrc, ScRangeList *pDest,
192 int nMax, std::vector<ScMyAddress> &vecRet)
194 if (pSrc == nullptr || pDest == nullptr)
196 return false;
198 int nSize =0;
199 if (pDest->GetCellCount() == 0)//if the Dest Rang List is empty
201 if (pSrc->GetCellCount() > o3tl::make_unsigned(nMax))//if the Src Cell count is greater than nMax
203 return true;
205 //now the cell count is less than nMax
206 vecRet.reserve(10);
207 size_t nSrcSize = pSrc->size();
208 for (size_t i = 0; i < nSrcSize; ++i)
210 ScRange const & rRange = (*pSrc)[i];
211 for (sal_Int32 row = rRange.aStart.Row(); row <= rRange.aEnd.Row();++row)
213 for (sal_uInt16 col = rRange.aStart.Col(); col <= rRange.aEnd.Col();++col)
215 vecRet.emplace_back(col,row, rRange.aStart.Tab());
219 return false;
221 //the Dest Rang List is not empty
222 vecRet.reserve(10);
223 size_t nSizeSrc = pSrc->size();
224 for (size_t i = 0; i < nSizeSrc; ++i)
226 ScRange const & rRange = (*pSrc)[i];
227 size_t nSizeDest = pDest->size();
228 for (size_t j = 0; j < nSizeDest; ++j)
230 ScRange const & rRangeDest = (*pDest)[j];
231 if (CalcScRangeDifferenceMax(rRange,rRangeDest,nMax,vecRet,nSize))
233 return true;
237 return false;
240 //===== internal ============================================================
242 // FIXME: really unclear why we have an ScAccessibleTableBase with
243 // only this single sub-class
244 ScAccessibleSpreadsheet::ScAccessibleSpreadsheet(
245 ScAccessibleDocument* pAccDoc,
246 ScTabViewShell* pViewShell,
247 SCTAB nTab,
248 ScSplitPos eSplitPos)
250 ScAccessibleTableBase( pAccDoc, GetDocument(pViewShell), ScRange( 0, 0, nTab, GetDocument(pViewShell)->MaxCol(), GetDocument(pViewShell)->MaxRow(), nTab)),
251 mbIsSpreadsheet( true ),
252 m_bFormulaMode( false ),
253 m_bFormulaLastMode( false ),
254 m_nMinX(0),m_nMaxX(0),m_nMinY(0),m_nMaxY(0)
256 ConstructScAccessibleSpreadsheet( pAccDoc, pViewShell, nTab, eSplitPos );
259 ScAccessibleSpreadsheet::ScAccessibleSpreadsheet(
260 ScAccessibleSpreadsheet& rParent, const ScRange& rRange ) :
261 ScAccessibleTableBase( rParent.mpAccDoc, rParent.mpDoc, rRange),
262 mbIsSpreadsheet( false ),
263 m_bFormulaMode( false ),
264 m_bFormulaLastMode( false ),
265 m_nMinX(0),m_nMaxX(0),m_nMinY(0),m_nMaxY(0)
267 ConstructScAccessibleSpreadsheet( rParent.mpAccDoc, rParent.mpViewShell, rParent.mnTab, rParent.meSplitPos );
270 ScAccessibleSpreadsheet::~ScAccessibleSpreadsheet()
272 mpMarkedRanges.reset();
273 if (mpViewShell)
274 mpViewShell->RemoveAccessibilityObject(*this);
277 void ScAccessibleSpreadsheet::ConstructScAccessibleSpreadsheet(
278 ScAccessibleDocument* pAccDoc,
279 ScTabViewShell* pViewShell,
280 SCTAB nTab,
281 ScSplitPos eSplitPos)
283 mpViewShell = pViewShell;
284 mpMarkedRanges = nullptr;
285 mpAccDoc = pAccDoc;
286 mpAccCell.clear();
287 meSplitPos = eSplitPos;
288 mnTab = nTab;
289 mbDelIns = false;
290 mbIsFocusSend = false;
291 if (!mpViewShell)
292 return;
294 mpViewShell->AddAccessibilityObject(*this);
296 const ScViewData& rViewData = mpViewShell->GetViewData();
297 maActiveCell = rViewData.GetCurPos();
298 mpAccCell = GetAccessibleCellAt(maActiveCell.Row(), maActiveCell.Col());
299 ScDocument* pScDoc= GetDocument(mpViewShell);
300 if (pScDoc)
302 pScDoc->GetName( maActiveCell.Tab(), m_strOldTabName );
306 void SAL_CALL ScAccessibleSpreadsheet::disposing()
308 SolarMutexGuard aGuard;
309 if (mpViewShell)
311 mpViewShell->RemoveAccessibilityObject(*this);
312 mpViewShell = nullptr;
315 mpAccCell.clear();
316 m_mapSelectionSend.clear();
317 m_mapFormulaSelectionSend.clear();
318 m_pAccFormulaCell.clear();
319 m_mapCells.clear();
321 ScAccessibleTableBase::disposing();
324 void ScAccessibleSpreadsheet::CompleteSelectionChanged(bool bNewState)
326 if (IsFormulaMode())
328 return ;
330 mpMarkedRanges.reset();
332 AccessibleEventObject aEvent;
333 aEvent.EventId = AccessibleEventId::STATE_CHANGED;
334 if (bNewState)
335 aEvent.NewValue <<= AccessibleStateType::SELECTED;
336 else
337 aEvent.OldValue <<= AccessibleStateType::SELECTED;
338 aEvent.Source = uno::Reference< XAccessibleContext >(this);
340 CommitChange(aEvent);
343 void ScAccessibleSpreadsheet::LostFocus()
345 AccessibleEventObject aEvent;
346 aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED;
347 aEvent.Source = uno::Reference< XAccessibleContext >(this);
348 aEvent.OldValue <<= uno::Reference<XAccessible>(mpAccCell);
350 CommitChange(aEvent);
352 CommitFocusLost();
355 void ScAccessibleSpreadsheet::GotFocus()
357 CommitFocusGained();
358 AccessibleEventObject aEvent;
359 aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED;
360 aEvent.Source = uno::Reference< XAccessibleContext >(this);
361 uno::Reference< XAccessible > xNew;
362 if (IsFormulaMode())
364 if (!m_pAccFormulaCell.is() || !m_bFormulaLastMode)
366 ScAddress aFormulaAddr;
367 if(!GetFormulaCurrentFocusCell(aFormulaAddr))
369 return;
371 m_pAccFormulaCell = GetAccessibleCellAt(aFormulaAddr.Row(),aFormulaAddr.Col());
373 xNew = m_pAccFormulaCell.get();
375 else
377 if(mpAccCell->GetCellAddress() == maActiveCell)
379 xNew = mpAccCell.get();
381 else
383 CommitFocusCell(maActiveCell);
384 return ;
387 aEvent.NewValue <<= xNew;
389 CommitChange(aEvent);
392 void ScAccessibleSpreadsheet::BoundingBoxChanged()
394 AccessibleEventObject aEvent;
395 aEvent.EventId = AccessibleEventId::BOUNDRECT_CHANGED;
396 aEvent.Source = uno::Reference< XAccessibleContext >(this);
398 CommitChange(aEvent);
401 void ScAccessibleSpreadsheet::VisAreaChanged()
403 AccessibleEventObject aEvent;
404 aEvent.EventId = AccessibleEventId::VISIBLE_DATA_CHANGED;
405 aEvent.Source = uno::Reference< XAccessibleContext >(this);
407 CommitChange(aEvent);
410 //===== SfxListener =====================================================
412 void ScAccessibleSpreadsheet::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
414 if ( rHint.GetId() == SfxHintId::ScUpdateRef )
416 auto pRefHint = static_cast<const ScUpdateRefHint*>(&rHint);
417 if (pRefHint->GetMode() == URM_INSDEL && pRefHint->GetDz() == 0) //test whether table is inserted or deleted
419 if (((pRefHint->GetRange().aStart.Col() == maRange.aStart.Col()) &&
420 (pRefHint->GetRange().aEnd.Col() == maRange.aEnd.Col())) ||
421 ((pRefHint->GetRange().aStart.Row() == maRange.aStart.Row()) &&
422 (pRefHint->GetRange().aEnd.Row() == maRange.aEnd.Row())))
424 // ignore next SfxHintId::ScDataChanged notification
425 mbDelIns = true;
427 SCROW nFirstRow = -1;
428 SCROW nLastRow = -1;
429 SCCOL nFirstCol = -1;
430 SCCOL nLastCol = -1;
432 sal_Int16 nId(0);
433 SCCOL nX(pRefHint->GetDx());
434 SCROW nY(pRefHint->GetDy());
435 ScRange aRange(pRefHint->GetRange());
436 if ((nX < 0) || (nY < 0))
438 assert(!((nX < 0) && (nY < 0)) && "should not be possible to remove row and column at the same time");
440 // Range in the update hint is the range after the removed rows/columns;
441 // calculate indices for the removed ones from that
442 if (nX < 0)
444 nId = AccessibleTableModelChangeType::COLUMNS_REMOVED;
445 nFirstCol = aRange.aStart.Col() + nX;
446 nLastCol = aRange.aStart.Col() - 1;
448 else
450 nId = AccessibleTableModelChangeType::ROWS_REMOVED;
451 nFirstRow = aRange.aStart.Row() + nY;
452 nLastRow = aRange.aStart.Row() - 1;
455 else if ((nX > 0) || (nY > 0))
457 assert(!((nX > 0) && (nY > 0)) && "should not be possible to add row and column at the same time");
459 // Range in the update hint is from first inserted row/column to last one in spreadsheet;
460 // calculate indices for the inserted ones from that
461 if (nX > 0)
463 nId = AccessibleTableModelChangeType::COLUMNS_INSERTED;
464 nFirstCol = aRange.aStart.Col();
465 nLastCol = aRange.aStart.Col() + nX - 1;
467 else
469 nId = AccessibleTableModelChangeType::ROWS_INSERTED;
470 nFirstRow = aRange.aStart.Row();
471 nLastRow = aRange.aStart.Row() + nY -1;
474 else
476 assert(false && "is it a deletion or an insertion?");
479 CommitTableModelChange(nFirstRow, nFirstCol, nLastRow, nLastCol, nId);
481 AccessibleEventObject aEvent;
482 aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED;
483 aEvent.Source = uno::Reference< XAccessibleContext >(this);
484 aEvent.NewValue <<= uno::Reference<XAccessible>(mpAccCell);
486 CommitChange(aEvent);
490 else
492 if (rHint.GetId() == SfxHintId::ScAccCursorChanged)
494 if (mpViewShell)
496 ScViewData& rViewData = mpViewShell->GetViewData();
498 m_bFormulaMode = rViewData.IsRefMode() || ScModule::get()->IsFormulaMode();
499 if ( m_bFormulaMode )
501 NotifyRefMode();
502 m_bFormulaLastMode = true;
503 return;
505 if (m_bFormulaLastMode)
506 {//Last Notify Mode Is Formula Mode.
507 m_vecFormulaLastMyAddr.clear();
508 RemoveFormulaSelection(true);
509 m_pAccFormulaCell.clear();
510 //Remove All Selection
512 m_bFormulaLastMode = m_bFormulaMode;
514 AccessibleEventObject aEvent;
515 aEvent.Source = uno::Reference< XAccessible >(this);
516 ScAddress aNewCell = rViewData.GetCurPos();
517 if(aNewCell.Tab() != maActiveCell.Tab())
519 aEvent.EventId = AccessibleEventId::PAGE_CHANGED;
520 auto pAccParent = getAccessibleParent();
521 ScAccessibleDocument *pAccDoc =
522 static_cast<ScAccessibleDocument*>(pAccParent.get());
523 if(pAccDoc)
525 pAccDoc->CommitChange(aEvent);
528 bool bNewPosCell = (aNewCell != maActiveCell) || mpViewShell->GetForceFocusOnCurCell(); // #i123629#
529 bool bNewPosCellFocus=false;
530 if ( bNewPosCell && IsFocused() && aNewCell.Tab() == maActiveCell.Tab() )
531 {//single Focus
532 bNewPosCellFocus=true;
534 ScMarkData &refScMarkData = rViewData.GetMarkData();
535 // MT IA2: Not used
536 // int nSelCount = refScMarkData.GetSelectCount();
537 bool bIsMark =refScMarkData.IsMarked();
538 bool bIsMultMark = refScMarkData.IsMultiMarked();
539 bool bNewMarked = refScMarkData.GetTableSelect(aNewCell.Tab()) && ( bIsMark || bIsMultMark );
540 // sal_Bool bNewCellSelected = isAccessibleSelected(aNewCell.Row(), aNewCell.Col());
541 sal_uInt16 nTab = rViewData.GetTabNo();
542 const ScRange& aMarkRange = refScMarkData.GetMarkArea();
543 aEvent.OldValue.clear();
544 ScDocument* pDoc= GetDocument(mpViewShell);
545 //Mark All
546 if ( !bNewPosCellFocus &&
547 (bNewMarked || bIsMark || bIsMultMark ) &&
548 aMarkRange == ScRange( 0,0,nTab, pDoc->MaxCol(),pDoc->MaxRow(),nTab ) )
550 aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_WITHIN;
551 aEvent.NewValue.clear();
552 CommitChange(aEvent);
553 return ;
555 if (!mpMarkedRanges)
557 mpMarkedRanges.reset(new ScRangeList());
559 refScMarkData.FillRangeListWithMarks(mpMarkedRanges.get(), true);
561 //For Whole Col Row
562 bool bWholeRow = std::abs(aMarkRange.aStart.Row() - aMarkRange.aEnd.Row()) == pDoc->MaxRow() ;
563 bool bWholeCol = ::abs(aMarkRange.aStart.Col() - aMarkRange.aEnd.Col()) == pDoc->MaxCol() ;
564 if ((bNewMarked || bIsMark || bIsMultMark ) && (bWholeCol || bWholeRow))
566 if ( aMarkRange != m_aLastWithInMarkRange )
568 RemoveSelection(refScMarkData);
569 if(bNewPosCell)
571 CommitFocusCell(aNewCell);
573 bool bLastIsWholeColRow =
574 (std::abs(m_aLastWithInMarkRange.aStart.Row() - m_aLastWithInMarkRange.aEnd.Row()) == pDoc->MaxRow() && bWholeRow) ||
575 (::abs(m_aLastWithInMarkRange.aStart.Col() - m_aLastWithInMarkRange.aEnd.Col()) == pDoc->MaxCol() && bWholeCol);
576 bool bSelSmaller=
577 bLastIsWholeColRow &&
578 !aMarkRange.Contains(m_aLastWithInMarkRange) &&
579 aMarkRange.Intersects(m_aLastWithInMarkRange);
580 if( !bSelSmaller )
582 aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_WITHIN;
583 aEvent.NewValue.clear();
584 CommitChange(aEvent);
586 m_aLastWithInMarkRange = aMarkRange;
588 return ;
590 m_aLastWithInMarkRange = aMarkRange;
591 int nNewMarkCount = mpMarkedRanges->GetCellCount();
592 bool bSendSingle= (0 == nNewMarkCount) && bNewPosCell;
593 if (bSendSingle)
595 RemoveSelection(refScMarkData);
596 if(bNewPosCellFocus)
598 CommitFocusCell(aNewCell);
600 uno::Reference< XAccessible > xChild ;
601 if (bNewPosCellFocus)
603 xChild = mpAccCell.get();
605 else
607 mpAccCell = GetAccessibleCellAt(aNewCell.Row(),aNewCell.Col());
608 xChild = mpAccCell.get();
610 maActiveCell = aNewCell;
611 aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED_NOFOCUS;
612 aEvent.NewValue <<= xChild;
613 aEvent.OldValue <<= uno::Reference< XAccessible >();
614 CommitChange(aEvent);
616 aEvent.EventId = AccessibleEventId::SELECTION_CHANGED;
617 aEvent.NewValue <<= xChild;
618 CommitChange(aEvent);
619 OSL_ASSERT(m_mapSelectionSend.count(aNewCell) == 0 );
620 m_mapSelectionSend.emplace(aNewCell,xChild);
623 else
625 ScRange aDelRange;
626 bool bIsDel = rViewData.GetDelMark( aDelRange );
627 if ( (!bIsDel || aMarkRange != aDelRange) &&
628 bNewMarked &&
629 nNewMarkCount > 0 &&
630 m_LastMarkedRanges != *mpMarkedRanges )
632 RemoveSelection(refScMarkData);
633 if(bNewPosCellFocus)
635 CommitFocusCell(aNewCell);
637 std::vector<ScMyAddress> vecNew;
638 if(CalcScRangeListDifferenceMax(mpMarkedRanges.get(), &m_LastMarkedRanges,10,vecNew))
640 aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_WITHIN;
641 aEvent.NewValue.clear();
642 CommitChange(aEvent);
644 else
646 for(const auto& rAddr : vecNew)
648 uno::Reference< XAccessible > xChild = getAccessibleCellAt(rAddr.Row(),rAddr.Col());
649 if (!(bNewPosCellFocus && rAddr == aNewCell) )
651 aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED_NOFOCUS;
652 aEvent.NewValue <<= xChild;
653 CommitChange(aEvent);
655 aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_ADD;
656 aEvent.NewValue <<= xChild;
657 CommitChange(aEvent);
658 m_mapSelectionSend.emplace(rAddr,xChild);
663 if (bNewPosCellFocus && maActiveCell != aNewCell)
665 CommitFocusCell(aNewCell);
667 m_LastMarkedRanges = *mpMarkedRanges;
670 else if (rHint.GetId() == SfxHintId::ScDataChanged)
672 if (!mbDelIns)
673 CommitTableModelChange(maRange.aStart.Row(), maRange.aStart.Col(), maRange.aEnd.Row(), maRange.aEnd.Col(), AccessibleTableModelChangeType::UPDATE);
674 else
675 mbDelIns = false;
676 if (mpViewShell)
678 ScViewData& rViewData = mpViewShell->GetViewData();
679 ScAddress aNewCell = rViewData.GetCurPos();
680 if( maActiveCell == aNewCell)
682 ScDocument* pScDoc= GetDocument(mpViewShell);
683 if (pScDoc)
685 OUString valStr(pScDoc->GetString(aNewCell.Col(),aNewCell.Row(),aNewCell.Tab()));
686 if(mpAccCell.is() && m_strCurCellValue != valStr)
688 AccessibleEventObject aTextChangedEvent;
689 (void)comphelper::OCommonAccessibleText::implInitTextChangedEvent(m_strCurCellValue, valStr,
690 aTextChangedEvent.OldValue,
691 aTextChangedEvent.NewValue);
692 aTextChangedEvent.EventId = AccessibleEventId::TEXT_CHANGED;
693 mpAccCell->CommitChange(aTextChangedEvent);
695 if (pScDoc->HasValueData(maActiveCell))
697 AccessibleEventObject aEvent;
698 aEvent.EventId = AccessibleEventId::VALUE_CHANGED;
699 mpAccCell->CommitChange(aEvent);
702 m_strCurCellValue = valStr;
704 OUString tabName;
705 pScDoc->GetName( maActiveCell.Tab(), tabName );
706 if( m_strOldTabName != tabName )
708 AccessibleEventObject aEvent;
709 aEvent.EventId = AccessibleEventId::NAME_CHANGED;
710 OUString sOldName(ScResId(STR_ACC_TABLE_NAME));
711 sOldName = sOldName.replaceFirst("%1", m_strOldTabName);
712 aEvent.OldValue <<= sOldName;
713 OUString sNewName(ScResId(STR_ACC_TABLE_NAME));
714 sNewName = sNewName.replaceFirst("%1", tabName);
715 aEvent.NewValue <<= sNewName;
716 CommitChange( aEvent );
717 m_strOldTabName = tabName;
723 // commented out, because to use a ModelChangeEvent is not the right way
724 // at the moment there is no way, but the Java/Gnome Api should be extended sometime
725 /* if (mpViewShell)
727 Rectangle aNewVisCells(GetVisCells(GetVisArea(mpViewShell, meSplitPos)));
729 Rectangle aNewPos(aNewVisCells);
731 if (aNewVisCells.Overlaps(maVisCells))
732 aNewPos.Union(maVisCells);
733 else
734 CommitTableModelChange(maVisCells.Top(), maVisCells.Left(), maVisCells.Bottom(), maVisCells.Right(), AccessibleTableModelChangeType::UPDATE);
736 maVisCells = aNewVisCells;
738 CommitTableModelChange(aNewPos.Top(), aNewPos.Left(), aNewPos.Bottom(), aNewPos.Right(), AccessibleTableModelChangeType::UPDATE);
743 ScAccessibleTableBase::Notify(rBC, rHint);
746 void ScAccessibleSpreadsheet::RemoveSelection(const ScMarkData &refScMarkData)
748 AccessibleEventObject aEvent;
749 aEvent.Source = uno::Reference< XAccessible >(this);
750 MAP_ADDR_XACC::iterator miRemove = m_mapSelectionSend.begin();
751 while (miRemove != m_mapSelectionSend.end())
753 if (refScMarkData.IsCellMarked(miRemove->first.Col(),miRemove->first.Row(),true) ||
754 refScMarkData.IsCellMarked(miRemove->first.Col(),miRemove->first.Row()) )
756 ++miRemove;
757 continue;
759 aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_REMOVE;
760 aEvent.NewValue <<= miRemove->second;
761 CommitChange(aEvent);
762 miRemove = m_mapSelectionSend.erase(miRemove);
765 void ScAccessibleSpreadsheet::CommitFocusCell(const ScAddress &aNewCell)
767 OSL_ASSERT(!IsFormulaMode());
768 if(IsFormulaMode())
770 return ;
773 // Related tdf#158914: check if focused cell has changed
774 // Some accessibility tools, such as NVDA on Windows, appear to need
775 // a value changed notification before it will refetch a cell's
776 // accessible text. While Notify() is able to fire text and value changed
777 // notifications, it seems to be rarely called and when it is called,
778 // such as when undoing a cell, it can be called before the cell's
779 // accessible text has been updated.
780 // So before a cell loses focus, check if any accessible text changes
781 // have occurred and fire text and value changed notifications if needed.
782 ScDocument* pScDoc= GetDocument(mpViewShell);
783 if (pScDoc && mpAccCell.is())
785 const ScAddress aOldActiveCell = mpAccCell->GetCellAddress();
786 OUString valStr(pScDoc->GetString(aOldActiveCell.Col(),aOldActiveCell.Row(),aOldActiveCell.Tab()));
787 if(m_strCurCellValue != valStr)
789 AccessibleEventObject aTextChangedEvent;
790 (void)comphelper::OCommonAccessibleText::implInitTextChangedEvent(m_strCurCellValue, valStr,
791 aTextChangedEvent.OldValue,
792 aTextChangedEvent.NewValue);
793 aTextChangedEvent.EventId = AccessibleEventId::TEXT_CHANGED;
794 mpAccCell->CommitChange(aTextChangedEvent);
796 if (pScDoc->HasValueData(maActiveCell))
798 AccessibleEventObject aEvent;
799 aEvent.EventId = AccessibleEventId::VALUE_CHANGED;
800 mpAccCell->CommitChange(aEvent);
803 m_strCurCellValue = valStr;
807 AccessibleEventObject aEvent;
808 aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED;
809 aEvent.Source = uno::Reference< XAccessible >(this);
810 aEvent.OldValue <<= uno::Reference<XAccessible>(mpAccCell);
811 mpAccCell.clear();
812 mpAccCell = GetAccessibleCellAt(aNewCell.Row(), aNewCell.Col());
813 aEvent.NewValue <<= uno::Reference<XAccessible>(mpAccCell);
814 maActiveCell = aNewCell;
815 if (pScDoc)
817 m_strCurCellValue = pScDoc->GetString(maActiveCell.Col(),maActiveCell.Row(),maActiveCell.Tab());
819 CommitChange(aEvent);
822 //===== XAccessibleTable ================================================
824 uno::Reference< XAccessibleTable > SAL_CALL ScAccessibleSpreadsheet::getAccessibleRowHeaders( )
826 SolarMutexGuard aGuard;
827 IsObjectValid();
828 uno::Reference< XAccessibleTable > xAccessibleTable;
829 if( mpDoc && mbIsSpreadsheet )
831 if( std::optional<ScRange> oRowRange = mpDoc->GetRepeatRowRange( mnTab ) )
833 SCROW nStart = oRowRange->aStart.Row();
834 SCROW nEnd = oRowRange->aEnd.Row();
835 ScDocument* pDoc = GetDocument(mpViewShell);
836 if( (0 <= nStart) && (nStart <= nEnd) && (nEnd <= pDoc->MaxRow()) )
837 xAccessibleTable.set( new ScAccessibleSpreadsheet( *this, ScRange( 0, nStart, mnTab, pDoc->MaxCol(), nEnd, mnTab ) ) );
840 return xAccessibleTable;
843 uno::Reference< XAccessibleTable > SAL_CALL ScAccessibleSpreadsheet::getAccessibleColumnHeaders( )
845 SolarMutexGuard aGuard;
846 IsObjectValid();
847 uno::Reference< XAccessibleTable > xAccessibleTable;
848 if( mpDoc && mbIsSpreadsheet )
850 if( std::optional<ScRange> oColRange = mpDoc->GetRepeatColRange( mnTab ) )
852 SCCOL nStart = oColRange->aStart.Col();
853 SCCOL nEnd = oColRange->aEnd.Col();
854 ScDocument* pDoc = GetDocument(mpViewShell);
855 if( (0 <= nStart) && (nStart <= nEnd) && (nEnd <= pDoc->MaxCol()) )
856 xAccessibleTable.set( new ScAccessibleSpreadsheet( *this, ScRange( nStart, 0, mnTab, nEnd, pDoc->MaxRow(), mnTab ) ) );
859 return xAccessibleTable;
862 uno::Sequence< sal_Int32 > SAL_CALL ScAccessibleSpreadsheet::getSelectedAccessibleRows( )
864 SolarMutexGuard aGuard;
865 IsObjectValid();
866 uno::Sequence<sal_Int32> aSequence;
867 if (IsFormulaMode())
869 return aSequence;
871 if (mpViewShell)
873 aSequence.realloc(maRange.aEnd.Row() - maRange.aStart.Row() + 1);
874 const ScMarkData& rMarkdata = mpViewShell->GetViewData().GetMarkData();
875 sal_Int32* pSequence = aSequence.getArray();
876 sal_Int32 nCount(0);
877 for (SCROW i = maRange.aStart.Row(); i <= maRange.aEnd.Row(); ++i)
879 if (rMarkdata.IsRowMarked(i))
881 pSequence[nCount] = i;
882 ++nCount;
885 aSequence.realloc(nCount);
887 else
888 aSequence.realloc(0);
889 return aSequence;
892 uno::Sequence< sal_Int32 > SAL_CALL ScAccessibleSpreadsheet::getSelectedAccessibleColumns( )
894 SolarMutexGuard aGuard;
895 IsObjectValid();
896 uno::Sequence<sal_Int32> aSequence;
897 if (IsFormulaMode() || !mpViewShell)
898 return aSequence;
900 aSequence.realloc(maRange.aEnd.Col() - maRange.aStart.Col() + 1);
901 sal_Int32* pSequence = aSequence.getArray();
902 sal_Int32 nCount(0);
903 const ScMarkData& rMarkdata = mpViewShell->GetViewData().GetMarkData();
904 for (SCCOL i = maRange.aStart.Col(); i <= maRange.aEnd.Col(); ++i)
906 if (rMarkdata.IsColumnMarked(i))
908 pSequence[nCount] = i;
909 ++nCount;
912 aSequence.realloc(nCount);
913 return aSequence;
916 sal_Bool SAL_CALL ScAccessibleSpreadsheet::isAccessibleRowSelected( sal_Int32 nRow )
918 SolarMutexGuard aGuard;
919 IsObjectValid();
920 if (IsFormulaMode())
922 return false;
925 if ((nRow > (maRange.aEnd.Row() - maRange.aStart.Row())) || (nRow < 0))
926 throw lang::IndexOutOfBoundsException();
928 bool bResult(false);
929 if (mpViewShell)
931 const ScMarkData& rMarkdata = mpViewShell->GetViewData().GetMarkData();
932 bResult = rMarkdata.IsRowMarked(static_cast<SCROW>(nRow));
934 return bResult;
937 sal_Bool SAL_CALL ScAccessibleSpreadsheet::isAccessibleColumnSelected( sal_Int32 nColumn )
939 SolarMutexGuard aGuard;
940 IsObjectValid();
942 if (IsFormulaMode())
944 return false;
946 if ((nColumn > (maRange.aEnd.Col() - maRange.aStart.Col())) || (nColumn < 0))
947 throw lang::IndexOutOfBoundsException();
949 bool bResult(false);
950 if (mpViewShell)
952 const ScMarkData& rMarkdata = mpViewShell->GetViewData().GetMarkData();
953 bResult = rMarkdata.IsColumnMarked(static_cast<SCCOL>(nColumn));
955 return bResult;
958 rtl::Reference<ScAccessibleCell> ScAccessibleSpreadsheet::GetAccessibleCellAt(sal_Int32 nRow, sal_Int32 nColumn)
960 if (IsFormulaMode())
962 ScAddress aCellAddress(static_cast<SCCOL>(nColumn), nRow, mpViewShell->GetViewData().GetTabNo());
963 if ((aCellAddress == m_aFormulaActiveCell) && m_pAccFormulaCell.is())
965 return m_pAccFormulaCell;
967 else
969 return ScAccessibleCell::create(this, mpViewShell, aCellAddress, GetAccessibleIndexFormula(nRow, nColumn), meSplitPos, mpAccDoc);
972 else
974 ScAddress aCellAddress(static_cast<SCCOL>(maRange.aStart.Col() + nColumn),
975 static_cast<SCROW>(maRange.aStart.Row() + nRow), maRange.aStart.Tab());
976 if ((aCellAddress == maActiveCell) && mpAccCell.is())
978 return mpAccCell;
980 else
982 // tdf#158914 add back reusing weakly cached ScAccessibleCells
983 // While commit 8e886993f32b7db11a99bdecf06451e6de6c3842
984 // fixed tdf#157568, moving the selected cell rapidly creates
985 // a large number of stale ScAccessibleCell instances that
986 // aren't deleted until the Calc document is closed. So reduce
987 // memory usage by adding back the ScAccessibleCell cache that
988 // was in commit f22cb3dfab413a2917cd810b8e1b8f644a016327 now
989 // that a new fix for tdf#157568 has been implemented.
990 rtl::Reference<ScAccessibleCell> xCell;
991 auto it = m_mapCells.find(aCellAddress);
992 if (it != m_mapCells.end())
993 xCell = it->second.get();
994 if (xCell)
995 return xCell;
996 xCell = ScAccessibleCell::create(this, mpViewShell, aCellAddress, getAccessibleIndex(nRow, nColumn), meSplitPos, mpAccDoc);
997 m_mapCells.insert(std::make_pair(aCellAddress, xCell));
998 return xCell;
1003 uno::Reference< XAccessible > SAL_CALL ScAccessibleSpreadsheet::getAccessibleCellAt( sal_Int32 nRow, sal_Int32 nColumn )
1005 SolarMutexGuard aGuard;
1006 IsObjectValid();
1007 if (!IsFormulaMode())
1009 if (nRow > (maRange.aEnd.Row() - maRange.aStart.Row()) ||
1010 nRow < 0 ||
1011 nColumn > (maRange.aEnd.Col() - maRange.aStart.Col()) ||
1012 nColumn < 0)
1013 throw lang::IndexOutOfBoundsException();
1015 rtl::Reference<ScAccessibleCell> pAccessibleCell = GetAccessibleCellAt(nRow, nColumn);
1016 return pAccessibleCell;
1019 sal_Bool SAL_CALL ScAccessibleSpreadsheet::isAccessibleSelected( sal_Int32 nRow, sal_Int32 nColumn )
1021 SolarMutexGuard aGuard;
1022 IsObjectValid();
1024 if (IsFormulaMode())
1026 ScAddress addr(static_cast<SCCOL>(nColumn), nRow, 0);
1027 return IsScAddrFormulaSel(addr);
1029 if ((nColumn > (maRange.aEnd.Col() - maRange.aStart.Col())) || (nColumn < 0) ||
1030 (nRow > (maRange.aEnd.Row() - maRange.aStart.Row())) || (nRow < 0))
1031 throw lang::IndexOutOfBoundsException();
1033 bool bResult(false);
1034 if (mpViewShell)
1036 const ScMarkData& rMarkdata = mpViewShell->GetViewData().GetMarkData();
1037 bResult = rMarkdata.IsCellMarked(static_cast<SCCOL>(nColumn), static_cast<SCROW>(nRow));
1039 return bResult;
1042 //===== XAccessibleComponent ============================================
1044 uno::Reference< XAccessible > SAL_CALL ScAccessibleSpreadsheet::getAccessibleAtPoint(const awt::Point& rPoint)
1046 uno::Reference< XAccessible > xAccessible;
1047 if (containsPoint(rPoint))
1049 SolarMutexGuard aGuard;
1050 IsObjectValid();
1051 if (mpViewShell)
1053 SCCOL nX;
1054 SCROW nY;
1055 mpViewShell->GetViewData().GetPosFromPixel( rPoint.X, rPoint.Y, meSplitPos, nX, nY);
1056 try {
1057 xAccessible = getAccessibleCellAt(nY, nX);
1059 catch(const css::lang::IndexOutOfBoundsException &)
1061 return nullptr;
1065 return xAccessible;
1068 void SAL_CALL ScAccessibleSpreadsheet::grabFocus( )
1070 if (getAccessibleParent().is())
1072 uno::Reference<XAccessibleComponent> xAccessibleComponent(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY);
1073 if (xAccessibleComponent.is())
1074 xAccessibleComponent->grabFocus();
1078 sal_Int32 SAL_CALL ScAccessibleSpreadsheet::getForeground( )
1080 return sal_Int32(COL_BLACK);
1083 sal_Int32 SAL_CALL ScAccessibleSpreadsheet::getBackground( )
1085 SolarMutexGuard aGuard;
1086 IsObjectValid();
1087 return sal_Int32(ScModule::get()->GetColorConfig().GetColorValue(::svtools::DOCCOLOR).nColor);
1090 //===== XAccessibleContext ==============================================
1092 uno::Reference<XAccessibleRelationSet> SAL_CALL ScAccessibleSpreadsheet::getAccessibleRelationSet()
1094 rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSet;
1095 if(mpAccDoc)
1096 pRelationSet = mpAccDoc->GetRelationSet(nullptr);
1097 if (pRelationSet)
1098 return pRelationSet;
1099 return new utl::AccessibleRelationSetHelper();
1102 sal_Int64 SAL_CALL ScAccessibleSpreadsheet::getAccessibleStateSet()
1104 SolarMutexGuard aGuard;
1105 sal_Int64 nParentStates = 0;
1106 if (getAccessibleParent().is())
1108 uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
1109 nParentStates = xParentContext->getAccessibleStateSet();
1111 sal_Int64 nStateSet = 0;
1112 if (IsDefunc(nParentStates))
1113 nStateSet |= AccessibleStateType::DEFUNC;
1114 else
1116 nStateSet |= AccessibleStateType::MANAGES_DESCENDANTS;
1117 if (IsEditable())
1118 nStateSet |= AccessibleStateType::EDITABLE;
1119 nStateSet |= AccessibleStateType::ENABLED;
1120 nStateSet |= AccessibleStateType::FOCUSABLE;
1121 if (IsFocused())
1122 nStateSet |= AccessibleStateType::FOCUSED;
1123 nStateSet |= AccessibleStateType::MULTI_SELECTABLE;
1124 nStateSet |= AccessibleStateType::OPAQUE;
1125 nStateSet |= AccessibleStateType::SELECTABLE;
1126 if (IsCompleteSheetSelected())
1127 nStateSet |= AccessibleStateType::SELECTED;
1128 if (isShowing())
1129 nStateSet |= AccessibleStateType::SHOWING;
1130 if (isVisible())
1131 nStateSet |= AccessibleStateType::VISIBLE;
1133 return nStateSet;
1136 ///===== XAccessibleSelection ===========================================
1138 void SAL_CALL ScAccessibleSpreadsheet::selectAccessibleChild( sal_Int64 nChildIndex )
1140 SolarMutexGuard aGuard;
1141 IsObjectValid();
1142 if (nChildIndex < 0 || nChildIndex >= getAccessibleChildCount())
1143 throw lang::IndexOutOfBoundsException();
1145 if (mpViewShell)
1147 sal_Int32 nCol(getAccessibleColumn(nChildIndex));
1148 sal_Int32 nRow(getAccessibleRow(nChildIndex));
1150 SelectCell(nRow, nCol, false);
1154 void SAL_CALL
1155 ScAccessibleSpreadsheet::clearAccessibleSelection( )
1157 SolarMutexGuard aGuard;
1158 IsObjectValid();
1159 if (mpViewShell && !IsFormulaMode())
1160 mpViewShell->Unmark();
1163 void SAL_CALL ScAccessibleSpreadsheet::selectAllAccessibleChildren( )
1165 SolarMutexGuard aGuard;
1166 IsObjectValid();
1167 if (!mpViewShell)
1168 return;
1170 if (IsFormulaMode())
1172 ScDocument* pDoc = GetDocument(mpViewShell);
1173 ScViewData& rViewData = mpViewShell->GetViewData();
1174 mpViewShell->InitRefMode( 0, 0, rViewData.GetTabNo(), SC_REFTYPE_REF );
1175 rViewData.SetRefStart(0, 0, rViewData.GetTabNo());
1176 rViewData.SetRefEnd(pDoc->MaxCol(), pDoc->MaxRow(), rViewData.GetTabNo());
1177 mpViewShell->UpdateRef(pDoc->MaxCol(), pDoc->MaxRow(), rViewData.GetTabNo());
1179 else
1180 mpViewShell->SelectAll();
1183 sal_Int64 SAL_CALL
1184 ScAccessibleSpreadsheet::getSelectedAccessibleChildCount( )
1186 SolarMutexGuard aGuard;
1187 IsObjectValid();
1188 sal_Int64 nResult(0);
1189 if (mpViewShell)
1191 if (IsFormulaMode())
1193 nResult = static_cast<sal_Int64>(GetRowAll()) * static_cast<sal_Int64>(GetColAll());
1195 else
1197 if (!mpMarkedRanges)
1199 mpMarkedRanges.reset(new ScRangeList());
1200 ScMarkData aMarkData(mpViewShell->GetViewData().GetMarkData());
1201 aMarkData.FillRangeListWithMarks(mpMarkedRanges.get(), false);
1203 // is possible, because there shouldn't be overlapped ranges in it
1204 nResult = mpMarkedRanges->GetCellCount();
1207 return nResult;
1210 uno::Reference<XAccessible > SAL_CALL
1211 ScAccessibleSpreadsheet::getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex )
1213 SolarMutexGuard aGuard;
1214 IsObjectValid();
1215 uno::Reference < XAccessible > xAccessible;
1216 if (IsFormulaMode())
1218 if(CheckChildIndex(nSelectedChildIndex))
1220 ScAddress addr = GetChildIndexAddress(nSelectedChildIndex);
1221 xAccessible = getAccessibleCellAt(addr.Row(), addr.Col());
1223 return xAccessible;
1225 if (mpViewShell)
1227 if (!mpMarkedRanges)
1229 mpMarkedRanges.reset(new ScRangeList());
1230 mpViewShell->GetViewData().GetMarkData().FillRangeListWithMarks(mpMarkedRanges.get(), false);
1232 if ((nSelectedChildIndex < 0) ||
1233 (mpMarkedRanges->GetCellCount() <= o3tl::make_unsigned(nSelectedChildIndex)))
1235 throw lang::IndexOutOfBoundsException();
1237 ScMyAddress addr = CalcScAddressFromRangeList(mpMarkedRanges.get(),nSelectedChildIndex);
1238 auto it = m_mapSelectionSend.find(addr);
1239 if( it != m_mapSelectionSend.end() )
1240 xAccessible = it->second;
1241 else
1242 xAccessible = getAccessibleCellAt(addr.Row(), addr.Col());
1244 return xAccessible;
1247 void SAL_CALL ScAccessibleSpreadsheet::deselectAccessibleChild( sal_Int64 nChildIndex )
1249 SolarMutexGuard aGuard;
1250 IsObjectValid();
1252 if (nChildIndex < 0 || nChildIndex >= getAccessibleChildCount())
1253 throw lang::IndexOutOfBoundsException();
1255 if (!mpViewShell)
1256 return;
1258 sal_Int32 nCol(getAccessibleColumn(nChildIndex));
1259 sal_Int32 nRow(getAccessibleRow(nChildIndex));
1261 if (IsFormulaMode())
1263 if(IsScAddrFormulaSel(
1264 ScAddress(static_cast<SCCOL>(nCol), nRow,mpViewShell->GetViewData().GetTabNo()))
1267 SelectCell(nRow, nCol, true);
1269 return ;
1271 if (mpViewShell->GetViewData().GetMarkData().IsCellMarked(static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow)))
1272 SelectCell(nRow, nCol, true);
1275 void ScAccessibleSpreadsheet::SelectCell(sal_Int32 nRow, sal_Int32 nCol, bool bDeselect)
1277 if (IsFormulaMode())
1279 if (bDeselect)
1280 {//??
1281 return;
1283 else
1285 ScViewData& rViewData = mpViewShell->GetViewData();
1287 mpViewShell->InitRefMode( static_cast<SCCOL>(nCol), nRow, rViewData.GetTabNo(), SC_REFTYPE_REF );
1288 mpViewShell->UpdateRef(static_cast<SCCOL>(nCol), nRow, rViewData.GetTabNo());
1290 return ;
1292 mpViewShell->SetTabNo( maRange.aStart.Tab() );
1294 mpViewShell->DoneBlockMode( true ); // continue selecting
1295 mpViewShell->InitBlockMode( static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow), maRange.aStart.Tab(), bDeselect );
1297 mpViewShell->SelectionChanged();
1301 void ScAccessibleSpreadsheet::CreateSortedMarkedCells()
1303 mpSortedMarkedCells = new std::vector<ScMyAddress>();
1304 mpSortedMarkedCells->reserve(mpMarkedRanges->GetCellCount());
1305 for ( size_t i = 0, ListSize = mpMarkedRanges->size(); i < ListSize; ++i )
1307 ScRange* pRange = (*mpMarkedRanges)[i];
1308 if (pRange->aStart.Tab() != pRange->aEnd.Tab())
1310 if ((maActiveCell.Tab() >= pRange->aStart.Tab()) ||
1311 maActiveCell.Tab() <= pRange->aEnd.Tab())
1313 ScRange aRange(*pRange);
1314 aRange.aStart.SetTab(maActiveCell.Tab());
1315 aRange.aEnd.SetTab(maActiveCell.Tab());
1316 AddMarkedRange(aRange);
1318 else
1320 OSL_FAIL("Range of wrong table");
1323 else if(pRange->aStart.Tab() == maActiveCell.Tab())
1324 AddMarkedRange(*pRange);
1325 else
1327 OSL_FAIL("Range of wrong table");
1330 std::sort(mpSortedMarkedCells->begin(), mpSortedMarkedCells->end());
1333 void ScAccessibleSpreadsheet::AddMarkedRange(const ScRange& rRange)
1335 for (SCROW nRow = rRange.aStart.Row(); nRow <= rRange.aEnd.Row(); ++nRow)
1337 for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
1339 ScMyAddress aCell(nCol, nRow, maActiveCell.Tab());
1340 mpSortedMarkedCells->push_back(aCell);
1345 //===== XServiceInfo ====================================================
1347 OUString SAL_CALL ScAccessibleSpreadsheet::getImplementationName()
1349 return u"ScAccessibleSpreadsheet"_ustr;
1352 uno::Sequence< OUString> SAL_CALL
1353 ScAccessibleSpreadsheet::getSupportedServiceNames()
1355 const css::uno::Sequence<OUString> vals { u"com.sun.star.AccessibleSpreadsheet"_ustr };
1356 return comphelper::concatSequences(ScAccessibleContextBase::getSupportedServiceNames(), vals);
1359 //===== XTypeProvider =======================================================
1361 uno::Sequence<sal_Int8> SAL_CALL
1362 ScAccessibleSpreadsheet::getImplementationId()
1364 return css::uno::Sequence<sal_Int8>();
1367 ///===== XAccessibleEventBroadcaster =====================================
1369 void SAL_CALL ScAccessibleSpreadsheet::addAccessibleEventListener(const uno::Reference<XAccessibleEventListener>& xListener)
1371 SolarMutexGuard aGuard;
1372 IsObjectValid();
1373 ScAccessibleTableBase::addAccessibleEventListener(xListener);
1377 //==== internal =========================================================
1379 AbsoluteScreenPixelRectangle ScAccessibleSpreadsheet::GetBoundingBoxOnScreen() const
1381 AbsoluteScreenPixelRectangle aRect;
1382 if (mpViewShell)
1384 vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
1385 if (pWindow)
1386 aRect = pWindow->GetWindowExtentsAbsolute();
1388 return aRect;
1391 tools::Rectangle ScAccessibleSpreadsheet::GetBoundingBox() const
1393 tools::Rectangle aRect;
1394 if (mpViewShell)
1396 vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
1397 if (pWindow)
1398 //#101986#; extends to the same window, because the parent is the document and it has the same window
1399 aRect = pWindow->GetWindowExtentsRelative(*pWindow);
1401 return aRect;
1404 bool ScAccessibleSpreadsheet::IsDefunc(sal_Int64 nParentStates)
1406 return ScAccessibleContextBase::IsDefunc() || (mpViewShell == nullptr) || !getAccessibleParent().is() ||
1407 (nParentStates & AccessibleStateType::DEFUNC);
1410 bool ScAccessibleSpreadsheet::IsEditable()
1412 if (IsFormulaMode())
1414 return false;
1416 bool bProtected(false);
1417 if (mpDoc && mpDoc->IsTabProtected(maRange.aStart.Tab()))
1418 bProtected = true;
1419 return !bProtected;
1422 bool ScAccessibleSpreadsheet::IsFocused()
1424 bool bFocused(false);
1425 if (mpViewShell)
1427 if (mpViewShell->GetViewData().GetActivePart() == meSplitPos)
1428 bFocused = mpViewShell->GetActiveWin()->HasFocus();
1430 return bFocused;
1433 bool ScAccessibleSpreadsheet::IsCompleteSheetSelected()
1435 if (IsFormulaMode())
1437 return false;
1440 bool bResult(false);
1441 if(mpViewShell)
1443 //#103800#; use a copy of MarkData
1444 ScMarkData aMarkData(mpViewShell->GetViewData().GetMarkData());
1445 if (aMarkData.IsAllMarked(maRange))
1446 bResult = true;
1448 return bResult;
1451 ScDocument* ScAccessibleSpreadsheet::GetDocument(ScTabViewShell* pViewShell)
1453 ScDocument* pDoc = nullptr;
1454 if (pViewShell)
1455 pDoc = &pViewShell->GetViewData().GetDocument();
1456 return pDoc;
1459 sal_Bool SAL_CALL ScAccessibleSpreadsheet::selectRow( sal_Int32 row )
1461 SolarMutexGuard g;
1463 if (IsFormulaMode())
1465 return false;
1468 ScDocument* pDoc = GetDocument(mpViewShell);
1469 mpViewShell->SetTabNo( maRange.aStart.Tab() );
1470 mpViewShell->DoneBlockMode( true ); // continue selecting
1471 mpViewShell->InitBlockMode( 0, row, maRange.aStart.Tab(), false, false, true );
1472 mpViewShell->MarkCursor( pDoc->MaxCol(), row, maRange.aStart.Tab(), false, true );
1473 mpViewShell->SelectionChanged();
1474 return true;
1477 sal_Bool SAL_CALL ScAccessibleSpreadsheet::selectColumn( sal_Int32 column )
1479 SolarMutexGuard g;
1481 if (IsFormulaMode())
1483 return false;
1486 ScDocument* pDoc = GetDocument(mpViewShell);
1487 mpViewShell->SetTabNo( maRange.aStart.Tab() );
1488 mpViewShell->DoneBlockMode( true ); // continue selecting
1489 mpViewShell->InitBlockMode( static_cast<SCCOL>(column), 0, maRange.aStart.Tab(), false, true );
1490 mpViewShell->MarkCursor( static_cast<SCCOL>(column), pDoc->MaxRow(), maRange.aStart.Tab(), true );
1491 mpViewShell->SelectionChanged();
1492 return true;
1495 sal_Bool SAL_CALL ScAccessibleSpreadsheet::unselectRow( sal_Int32 row )
1497 SolarMutexGuard g;
1499 if (IsFormulaMode())
1501 return false;
1504 ScDocument* pDoc = GetDocument(mpViewShell);
1505 mpViewShell->SetTabNo( maRange.aStart.Tab() );
1506 mpViewShell->DoneBlockMode( true ); // continue selecting
1507 mpViewShell->InitBlockMode( 0, row, maRange.aStart.Tab(), false, false, true, true );
1508 mpViewShell->MarkCursor( pDoc->MaxCol(), row, maRange.aStart.Tab(), false, true );
1509 mpViewShell->SelectionChanged();
1510 mpViewShell->DoneBlockMode( true );
1511 return true;
1514 sal_Bool SAL_CALL ScAccessibleSpreadsheet::unselectColumn( sal_Int32 column )
1516 SolarMutexGuard g;
1518 if (IsFormulaMode())
1520 return false;
1523 ScDocument* pDoc = GetDocument(mpViewShell);
1524 mpViewShell->SetTabNo( maRange.aStart.Tab() );
1525 mpViewShell->DoneBlockMode( true ); // continue selecting
1526 mpViewShell->InitBlockMode( static_cast<SCCOL>(column), 0, maRange.aStart.Tab(), false, true, false, true );
1527 mpViewShell->MarkCursor( static_cast<SCCOL>(column), pDoc->MaxRow(), maRange.aStart.Tab(), true );
1528 mpViewShell->SelectionChanged();
1529 mpViewShell->DoneBlockMode( true );
1530 return true;
1533 void ScAccessibleSpreadsheet::FireFirstCellFocus()
1535 if (IsFormulaMode())
1537 return ;
1539 if (mbIsFocusSend)
1541 return ;
1543 mbIsFocusSend = true;
1544 AccessibleEventObject aEvent;
1545 aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED;
1546 aEvent.Source = uno::Reference< XAccessible >(this);
1547 aEvent.NewValue <<= getAccessibleCellAt(maActiveCell.Row(), maActiveCell.Col());
1548 CommitChange(aEvent);
1551 void ScAccessibleSpreadsheet::NotifyRefMode()
1553 ScViewData& rViewData = mpViewShell->GetViewData();
1554 if (!rViewData.IsRefMode())
1555 // Not in reference mode. Bail out.
1556 return;
1558 sal_uInt16 nRefStartX = rViewData.GetRefStartX();
1559 sal_Int32 nRefStartY = rViewData.GetRefStartY();
1560 sal_uInt16 nRefEndX = rViewData.GetRefEndX();
1561 sal_Int32 nRefEndY = rViewData.GetRefEndY();
1562 ScAddress aFormulaAddr;
1563 if(!GetFormulaCurrentFocusCell(aFormulaAddr))
1565 return ;
1567 if (m_aFormulaActiveCell != aFormulaAddr)
1568 {//New Focus
1569 m_nMinX =std::min(nRefStartX,nRefEndX);
1570 m_nMaxX =std::max(nRefStartX,nRefEndX);
1571 m_nMinY = std::min(nRefStartY,nRefEndY);
1572 m_nMaxY = std::max(nRefStartY,nRefEndY);
1573 RemoveFormulaSelection();
1574 AccessibleEventObject aEvent;
1575 aEvent.Source = uno::Reference< XAccessible >(this);
1576 aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED;
1577 aEvent.OldValue <<= uno::Reference<XAccessible>(m_pAccFormulaCell);
1578 m_pAccFormulaCell = GetAccessibleCellAt(aFormulaAddr.Row(), aFormulaAddr.Col());
1579 uno::Reference< XAccessible > xNew = m_pAccFormulaCell;
1580 aEvent.NewValue <<= xNew;
1581 CommitChange(aEvent);
1582 if (nRefStartX == nRefEndX && nRefStartY == nRefEndY)
1583 {//Selection Single
1584 aEvent.EventId = AccessibleEventId::SELECTION_CHANGED;
1585 aEvent.NewValue <<= xNew;
1586 CommitChange(aEvent);
1587 m_mapFormulaSelectionSend.emplace(aFormulaAddr,xNew);
1588 m_vecFormulaLastMyAddr.clear();
1589 m_vecFormulaLastMyAddr.emplace_back(aFormulaAddr);
1591 else
1593 std::vector<ScMyAddress> vecCurSel;
1594 int nCurSize = (m_nMaxX - m_nMinX +1)*(m_nMaxY - m_nMinY +1) ;
1595 vecCurSel.reserve(nCurSize);
1596 for (sal_uInt16 x = m_nMinX ; x <= m_nMaxX ; ++x)
1598 for (sal_Int32 y = m_nMinY ; y <= m_nMaxY ; ++y)
1600 ScMyAddress aAddr(x,y,0);
1601 vecCurSel.push_back(aAddr);
1604 std::sort(vecCurSel.begin(), vecCurSel.end());
1605 std::vector<ScMyAddress> vecNew;
1606 std::set_difference(vecCurSel.begin(),vecCurSel.end(),
1607 m_vecFormulaLastMyAddr.begin(),m_vecFormulaLastMyAddr.end(),
1608 std::back_insert_iterator(vecNew));
1609 int nNewSize = vecNew.size();
1610 if ( nNewSize > 10 )
1612 aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_WITHIN;
1613 aEvent.NewValue.clear();
1614 CommitChange(aEvent);
1616 else
1618 for(const auto& rAddr : vecNew)
1620 uno::Reference< XAccessible > xChild;
1621 if (rAddr == aFormulaAddr)
1623 xChild = m_pAccFormulaCell.get();
1625 else
1627 xChild = getAccessibleCellAt(rAddr.Row(),rAddr.Col());
1628 aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED_NOFOCUS;
1629 aEvent.NewValue <<= xChild;
1630 CommitChange(aEvent);
1632 aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_ADD;
1633 aEvent.NewValue <<= xChild;
1634 CommitChange(aEvent);
1635 m_mapFormulaSelectionSend.emplace(rAddr,xChild);
1638 m_vecFormulaLastMyAddr.swap(vecCurSel);
1641 m_aFormulaActiveCell = aFormulaAddr;
1644 void ScAccessibleSpreadsheet::RemoveFormulaSelection(bool bRemoveAll )
1646 AccessibleEventObject aEvent;
1647 aEvent.Source = uno::Reference< XAccessible >(this);
1648 MAP_ADDR_XACC::iterator miRemove = m_mapFormulaSelectionSend.begin();
1649 while (miRemove != m_mapFormulaSelectionSend.end())
1651 if( !bRemoveAll && IsScAddrFormulaSel(miRemove->first) )
1653 ++miRemove;
1654 continue;
1656 aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_REMOVE;
1657 aEvent.NewValue <<= miRemove->second;
1658 CommitChange(aEvent);
1659 miRemove = m_mapFormulaSelectionSend.erase(miRemove);
1663 bool ScAccessibleSpreadsheet::IsScAddrFormulaSel(const ScAddress &addr) const
1665 return addr.Col() >= m_nMinX && addr.Col() <= m_nMaxX &&
1666 addr.Row() >= m_nMinY && addr.Row() <= m_nMaxY &&
1667 addr.Tab() == mpViewShell->GetViewData().GetTabNo();
1670 bool ScAccessibleSpreadsheet::CheckChildIndex(sal_Int64 nIndex) const
1672 sal_Int64 nMaxIndex = static_cast<sal_Int64>(m_nMaxX - m_nMinX +1) * static_cast<sal_Int64>(m_nMaxY - m_nMinY +1) -1 ;
1673 return nIndex <= nMaxIndex && nIndex >= 0 ;
1676 ScAddress ScAccessibleSpreadsheet::GetChildIndexAddress(sal_Int64 nIndex) const
1678 sal_Int64 nRowAll = GetRowAll();
1679 sal_Int64 nColAll = GetColAll();
1680 if (nIndex < 0 || nIndex >= nRowAll * nColAll)
1682 return ScAddress();
1684 return ScAddress(
1685 static_cast<SCCOL>((nIndex - nIndex % nRowAll) / nRowAll + + m_nMinX),
1686 nIndex % nRowAll + m_nMinY,
1687 mpViewShell->GetViewData().GetTabNo()
1691 sal_Int64 ScAccessibleSpreadsheet::GetAccessibleIndexFormula( sal_Int32 nRow, sal_Int32 nColumn )
1693 sal_uInt16 nColRelative = sal_uInt16(nColumn) - GetColAll();
1694 sal_Int32 nRowRelative = nRow - GetRowAll();
1695 if (nRow < 0 || nColumn < 0 || nRowRelative >= GetRowAll() || nColRelative >= GetColAll() )
1697 return -1;
1699 return static_cast<sal_Int64>(GetRowAll()) * static_cast<sal_Int64>(nRowRelative) + nColRelative;
1702 bool ScAccessibleSpreadsheet::IsFormulaMode()
1704 ScViewData& rViewData = mpViewShell->GetViewData();
1705 m_bFormulaMode = rViewData.IsRefMode() || ScModule::get()->IsFormulaMode();
1706 return m_bFormulaMode ;
1709 bool ScAccessibleSpreadsheet::GetFormulaCurrentFocusCell(ScAddress &addr)
1711 ScViewData& rViewData = mpViewShell->GetViewData();
1712 sal_uInt16 nRefX=0;
1713 sal_Int32 nRefY=0;
1714 if(m_bFormulaLastMode)
1716 nRefX=rViewData.GetRefEndX();
1717 nRefY=rViewData.GetRefEndY();
1719 else
1721 nRefX=rViewData.GetRefStartX();
1722 nRefY=rViewData.GetRefStartY();
1724 ScDocument* pDoc = GetDocument(mpViewShell);
1725 if( /* Always true: nRefX >= 0 && */ nRefX <= pDoc->MaxCol() && nRefY >= 0 && nRefY <= pDoc->MaxRow())
1727 addr = ScAddress(nRefX,nRefY,rViewData.GetTabNo());
1728 return true;
1730 return false;
1733 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */