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 <AccessibleSpreadsheet.hxx>
21 #include <AccessibleCell.hxx>
22 #include <AccessibleDocument.hxx>
23 #include <tabvwsh.hxx>
24 #include <document.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>
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();
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());
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
);
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
);
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
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
)
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());
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
)
163 ScMyAddress
cell(col
,nMinRow
,rSrc
.aStart
.Tab());
164 if(!rDest
.Contains(cell
))
165 {//In Src ,Not In Dest
166 vecRet
.push_back(cell
);
170 if (nMinRow
!= nMaxRow
)
172 for (sal_uInt16 col
= rSrc
.aStart
.Col(); col
<= rSrc
.aEnd
.Col();++col
)
178 ScMyAddress
cell(col
,nMaxRow
,rSrc
.aStart
.Tab());
179 if(!rDest
.Contains(cell
))
180 {//In Src ,Not In Dest
181 vecRet
.push_back(cell
);
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)
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
205 //now the cell count is less than nMax
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());
221 //the Dest Rang List is not empty
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
))
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
,
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();
274 mpViewShell
->RemoveAccessibilityObject(*this);
277 void ScAccessibleSpreadsheet::ConstructScAccessibleSpreadsheet(
278 ScAccessibleDocument
* pAccDoc
,
279 ScTabViewShell
* pViewShell
,
281 ScSplitPos eSplitPos
)
283 mpViewShell
= pViewShell
;
284 mpMarkedRanges
= nullptr;
287 meSplitPos
= eSplitPos
;
290 mbIsFocusSend
= false;
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
);
302 pScDoc
->GetName( maActiveCell
.Tab(), m_strOldTabName
);
306 void SAL_CALL
ScAccessibleSpreadsheet::disposing()
308 SolarMutexGuard aGuard
;
311 mpViewShell
->RemoveAccessibilityObject(*this);
312 mpViewShell
= nullptr;
316 m_mapSelectionSend
.clear();
317 m_mapFormulaSelectionSend
.clear();
318 m_pAccFormulaCell
.clear();
321 ScAccessibleTableBase::disposing();
324 void ScAccessibleSpreadsheet::CompleteSelectionChanged(bool bNewState
)
330 mpMarkedRanges
.reset();
332 AccessibleEventObject aEvent
;
333 aEvent
.EventId
= AccessibleEventId::STATE_CHANGED
;
335 aEvent
.NewValue
<<= AccessibleStateType::SELECTED
;
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
);
355 void ScAccessibleSpreadsheet::GotFocus()
358 AccessibleEventObject aEvent
;
359 aEvent
.EventId
= AccessibleEventId::ACTIVE_DESCENDANT_CHANGED
;
360 aEvent
.Source
= uno::Reference
< XAccessibleContext
>(this);
361 uno::Reference
< XAccessible
> xNew
;
364 if (!m_pAccFormulaCell
.is() || !m_bFormulaLastMode
)
366 ScAddress aFormulaAddr
;
367 if(!GetFormulaCurrentFocusCell(aFormulaAddr
))
371 m_pAccFormulaCell
= GetAccessibleCellAt(aFormulaAddr
.Row(),aFormulaAddr
.Col());
373 xNew
= m_pAccFormulaCell
.get();
377 if(mpAccCell
->GetCellAddress() == maActiveCell
)
379 xNew
= mpAccCell
.get();
383 CommitFocusCell(maActiveCell
);
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
427 SCROW nFirstRow
= -1;
429 SCCOL nFirstCol
= -1;
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
444 nId
= AccessibleTableModelChangeType::COLUMNS_REMOVED
;
445 nFirstCol
= aRange
.aStart
.Col() + nX
;
446 nLastCol
= aRange
.aStart
.Col() - 1;
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
463 nId
= AccessibleTableModelChangeType::COLUMNS_INSERTED
;
464 nFirstCol
= aRange
.aStart
.Col();
465 nLastCol
= aRange
.aStart
.Col() + nX
- 1;
469 nId
= AccessibleTableModelChangeType::ROWS_INSERTED
;
470 nFirstRow
= aRange
.aStart
.Row();
471 nLastRow
= aRange
.aStart
.Row() + nY
-1;
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
);
492 if (rHint
.GetId() == SfxHintId::ScAccCursorChanged
)
496 ScViewData
& rViewData
= mpViewShell
->GetViewData();
498 m_bFormulaMode
= rViewData
.IsRefMode() || ScModule::get()->IsFormulaMode();
499 if ( m_bFormulaMode
)
502 m_bFormulaLastMode
= true;
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());
525 pAccDoc
->CommitChange(aEvent
);
528 bool bNewPosCell
= (aNewCell
!= maActiveCell
) || mpViewShell
->GetForceFocusOnCurCell(); // #i123629#
529 bool bNewPosCellFocus
=false;
530 if ( bNewPosCell
&& IsFocused() && aNewCell
.Tab() == maActiveCell
.Tab() )
532 bNewPosCellFocus
=true;
534 ScMarkData
&refScMarkData
= rViewData
.GetMarkData();
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
);
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
);
557 mpMarkedRanges
.reset(new ScRangeList());
559 refScMarkData
.FillRangeListWithMarks(mpMarkedRanges
.get(), true);
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
);
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
);
577 bLastIsWholeColRow
&&
578 !aMarkRange
.Contains(m_aLastWithInMarkRange
) &&
579 aMarkRange
.Intersects(m_aLastWithInMarkRange
);
582 aEvent
.EventId
= AccessibleEventId::SELECTION_CHANGED_WITHIN
;
583 aEvent
.NewValue
.clear();
584 CommitChange(aEvent
);
586 m_aLastWithInMarkRange
= aMarkRange
;
590 m_aLastWithInMarkRange
= aMarkRange
;
591 int nNewMarkCount
= mpMarkedRanges
->GetCellCount();
592 bool bSendSingle
= (0 == nNewMarkCount
) && bNewPosCell
;
595 RemoveSelection(refScMarkData
);
598 CommitFocusCell(aNewCell
);
600 uno::Reference
< XAccessible
> xChild
;
601 if (bNewPosCellFocus
)
603 xChild
= mpAccCell
.get();
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
);
626 bool bIsDel
= rViewData
.GetDelMark( aDelRange
);
627 if ( (!bIsDel
|| aMarkRange
!= aDelRange
) &&
630 m_LastMarkedRanges
!= *mpMarkedRanges
)
632 RemoveSelection(refScMarkData
);
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
);
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
)
673 CommitTableModelChange(maRange
.aStart
.Row(), maRange
.aStart
.Col(), maRange
.aEnd
.Row(), maRange
.aEnd
.Col(), AccessibleTableModelChangeType::UPDATE
);
678 ScViewData
& rViewData
= mpViewShell
->GetViewData();
679 ScAddress aNewCell
= rViewData
.GetCurPos();
680 if( maActiveCell
== aNewCell
)
682 ScDocument
* pScDoc
= GetDocument(mpViewShell
);
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
;
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
727 Rectangle aNewVisCells(GetVisCells(GetVisArea(mpViewShell, meSplitPos)));
729 Rectangle aNewPos(aNewVisCells);
731 if (aNewVisCells.Overlaps(maVisCells))
732 aNewPos.Union(maVisCells);
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()) )
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());
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
);
812 mpAccCell
= GetAccessibleCellAt(aNewCell
.Row(), aNewCell
.Col());
813 aEvent
.NewValue
<<= uno::Reference
<XAccessible
>(mpAccCell
);
814 maActiveCell
= aNewCell
;
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
;
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
;
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
;
866 uno::Sequence
<sal_Int32
> aSequence
;
873 aSequence
.realloc(maRange
.aEnd
.Row() - maRange
.aStart
.Row() + 1);
874 const ScMarkData
& rMarkdata
= mpViewShell
->GetViewData().GetMarkData();
875 sal_Int32
* pSequence
= aSequence
.getArray();
877 for (SCROW i
= maRange
.aStart
.Row(); i
<= maRange
.aEnd
.Row(); ++i
)
879 if (rMarkdata
.IsRowMarked(i
))
881 pSequence
[nCount
] = i
;
885 aSequence
.realloc(nCount
);
888 aSequence
.realloc(0);
892 uno::Sequence
< sal_Int32
> SAL_CALL
ScAccessibleSpreadsheet::getSelectedAccessibleColumns( )
894 SolarMutexGuard aGuard
;
896 uno::Sequence
<sal_Int32
> aSequence
;
897 if (IsFormulaMode() || !mpViewShell
)
900 aSequence
.realloc(maRange
.aEnd
.Col() - maRange
.aStart
.Col() + 1);
901 sal_Int32
* pSequence
= aSequence
.getArray();
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
;
912 aSequence
.realloc(nCount
);
916 sal_Bool SAL_CALL
ScAccessibleSpreadsheet::isAccessibleRowSelected( sal_Int32 nRow
)
918 SolarMutexGuard aGuard
;
925 if ((nRow
> (maRange
.aEnd
.Row() - maRange
.aStart
.Row())) || (nRow
< 0))
926 throw lang::IndexOutOfBoundsException();
931 const ScMarkData
& rMarkdata
= mpViewShell
->GetViewData().GetMarkData();
932 bResult
= rMarkdata
.IsRowMarked(static_cast<SCROW
>(nRow
));
937 sal_Bool SAL_CALL
ScAccessibleSpreadsheet::isAccessibleColumnSelected( sal_Int32 nColumn
)
939 SolarMutexGuard aGuard
;
946 if ((nColumn
> (maRange
.aEnd
.Col() - maRange
.aStart
.Col())) || (nColumn
< 0))
947 throw lang::IndexOutOfBoundsException();
952 const ScMarkData
& rMarkdata
= mpViewShell
->GetViewData().GetMarkData();
953 bResult
= rMarkdata
.IsColumnMarked(static_cast<SCCOL
>(nColumn
));
958 rtl::Reference
<ScAccessibleCell
> ScAccessibleSpreadsheet::GetAccessibleCellAt(sal_Int32 nRow
, sal_Int32 nColumn
)
962 ScAddress
aCellAddress(static_cast<SCCOL
>(nColumn
), nRow
, mpViewShell
->GetViewData().GetTabNo());
963 if ((aCellAddress
== m_aFormulaActiveCell
) && m_pAccFormulaCell
.is())
965 return m_pAccFormulaCell
;
969 return ScAccessibleCell::create(this, mpViewShell
, aCellAddress
, GetAccessibleIndexFormula(nRow
, nColumn
), meSplitPos
, mpAccDoc
);
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())
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();
996 xCell
= ScAccessibleCell::create(this, mpViewShell
, aCellAddress
, getAccessibleIndex(nRow
, nColumn
), meSplitPos
, mpAccDoc
);
997 m_mapCells
.insert(std::make_pair(aCellAddress
, xCell
));
1003 uno::Reference
< XAccessible
> SAL_CALL
ScAccessibleSpreadsheet::getAccessibleCellAt( sal_Int32 nRow
, sal_Int32 nColumn
)
1005 SolarMutexGuard aGuard
;
1007 if (!IsFormulaMode())
1009 if (nRow
> (maRange
.aEnd
.Row() - maRange
.aStart
.Row()) ||
1011 nColumn
> (maRange
.aEnd
.Col() - maRange
.aStart
.Col()) ||
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
;
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);
1036 const ScMarkData
& rMarkdata
= mpViewShell
->GetViewData().GetMarkData();
1037 bResult
= rMarkdata
.IsCellMarked(static_cast<SCCOL
>(nColumn
), static_cast<SCROW
>(nRow
));
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
;
1055 mpViewShell
->GetViewData().GetPosFromPixel( rPoint
.X
, rPoint
.Y
, meSplitPos
, nX
, nY
);
1057 xAccessible
= getAccessibleCellAt(nY
, nX
);
1059 catch(const css::lang::IndexOutOfBoundsException
&)
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
;
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
;
1096 pRelationSet
= mpAccDoc
->GetRelationSet(nullptr);
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
;
1116 nStateSet
|= AccessibleStateType::MANAGES_DESCENDANTS
;
1118 nStateSet
|= AccessibleStateType::EDITABLE
;
1119 nStateSet
|= AccessibleStateType::ENABLED
;
1120 nStateSet
|= AccessibleStateType::FOCUSABLE
;
1122 nStateSet
|= AccessibleStateType::FOCUSED
;
1123 nStateSet
|= AccessibleStateType::MULTI_SELECTABLE
;
1124 nStateSet
|= AccessibleStateType::OPAQUE
;
1125 nStateSet
|= AccessibleStateType::SELECTABLE
;
1126 if (IsCompleteSheetSelected())
1127 nStateSet
|= AccessibleStateType::SELECTED
;
1129 nStateSet
|= AccessibleStateType::SHOWING
;
1131 nStateSet
|= AccessibleStateType::VISIBLE
;
1136 ///===== XAccessibleSelection ===========================================
1138 void SAL_CALL
ScAccessibleSpreadsheet::selectAccessibleChild( sal_Int64 nChildIndex
)
1140 SolarMutexGuard aGuard
;
1142 if (nChildIndex
< 0 || nChildIndex
>= getAccessibleChildCount())
1143 throw lang::IndexOutOfBoundsException();
1147 sal_Int32
nCol(getAccessibleColumn(nChildIndex
));
1148 sal_Int32
nRow(getAccessibleRow(nChildIndex
));
1150 SelectCell(nRow
, nCol
, false);
1155 ScAccessibleSpreadsheet::clearAccessibleSelection( )
1157 SolarMutexGuard aGuard
;
1159 if (mpViewShell
&& !IsFormulaMode())
1160 mpViewShell
->Unmark();
1163 void SAL_CALL
ScAccessibleSpreadsheet::selectAllAccessibleChildren( )
1165 SolarMutexGuard aGuard
;
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());
1180 mpViewShell
->SelectAll();
1184 ScAccessibleSpreadsheet::getSelectedAccessibleChildCount( )
1186 SolarMutexGuard aGuard
;
1188 sal_Int64
nResult(0);
1191 if (IsFormulaMode())
1193 nResult
= static_cast<sal_Int64
>(GetRowAll()) * static_cast<sal_Int64
>(GetColAll());
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();
1210 uno::Reference
<XAccessible
> SAL_CALL
1211 ScAccessibleSpreadsheet::getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex
)
1213 SolarMutexGuard aGuard
;
1215 uno::Reference
< XAccessible
> xAccessible
;
1216 if (IsFormulaMode())
1218 if(CheckChildIndex(nSelectedChildIndex
))
1220 ScAddress addr
= GetChildIndexAddress(nSelectedChildIndex
);
1221 xAccessible
= getAccessibleCellAt(addr
.Row(), addr
.Col());
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
;
1242 xAccessible
= getAccessibleCellAt(addr
.Row(), addr
.Col());
1247 void SAL_CALL
ScAccessibleSpreadsheet::deselectAccessibleChild( sal_Int64 nChildIndex
)
1249 SolarMutexGuard aGuard
;
1252 if (nChildIndex
< 0 || nChildIndex
>= getAccessibleChildCount())
1253 throw lang::IndexOutOfBoundsException();
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);
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())
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());
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);
1320 OSL_FAIL("Range of wrong table");
1323 else if(pRange->aStart.Tab() == maActiveCell.Tab())
1324 AddMarkedRange(*pRange);
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
;
1373 ScAccessibleTableBase::addAccessibleEventListener(xListener
);
1377 //==== internal =========================================================
1379 AbsoluteScreenPixelRectangle
ScAccessibleSpreadsheet::GetBoundingBoxOnScreen() const
1381 AbsoluteScreenPixelRectangle aRect
;
1384 vcl::Window
* pWindow
= mpViewShell
->GetWindowByPos(meSplitPos
);
1386 aRect
= pWindow
->GetWindowExtentsAbsolute();
1391 tools::Rectangle
ScAccessibleSpreadsheet::GetBoundingBox() const
1393 tools::Rectangle aRect
;
1396 vcl::Window
* pWindow
= mpViewShell
->GetWindowByPos(meSplitPos
);
1398 //#101986#; extends to the same window, because the parent is the document and it has the same window
1399 aRect
= pWindow
->GetWindowExtentsRelative(*pWindow
);
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())
1416 bool bProtected(false);
1417 if (mpDoc
&& mpDoc
->IsTabProtected(maRange
.aStart
.Tab()))
1422 bool ScAccessibleSpreadsheet::IsFocused()
1424 bool bFocused(false);
1427 if (mpViewShell
->GetViewData().GetActivePart() == meSplitPos
)
1428 bFocused
= mpViewShell
->GetActiveWin()->HasFocus();
1433 bool ScAccessibleSpreadsheet::IsCompleteSheetSelected()
1435 if (IsFormulaMode())
1440 bool bResult(false);
1443 //#103800#; use a copy of MarkData
1444 ScMarkData
aMarkData(mpViewShell
->GetViewData().GetMarkData());
1445 if (aMarkData
.IsAllMarked(maRange
))
1451 ScDocument
* ScAccessibleSpreadsheet::GetDocument(ScTabViewShell
* pViewShell
)
1453 ScDocument
* pDoc
= nullptr;
1455 pDoc
= &pViewShell
->GetViewData().GetDocument();
1459 sal_Bool SAL_CALL
ScAccessibleSpreadsheet::selectRow( sal_Int32 row
)
1463 if (IsFormulaMode())
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();
1477 sal_Bool SAL_CALL
ScAccessibleSpreadsheet::selectColumn( sal_Int32 column
)
1481 if (IsFormulaMode())
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();
1495 sal_Bool SAL_CALL
ScAccessibleSpreadsheet::unselectRow( sal_Int32 row
)
1499 if (IsFormulaMode())
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 );
1514 sal_Bool SAL_CALL
ScAccessibleSpreadsheet::unselectColumn( sal_Int32 column
)
1518 if (IsFormulaMode())
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 );
1533 void ScAccessibleSpreadsheet::FireFirstCellFocus()
1535 if (IsFormulaMode())
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.
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
))
1567 if (m_aFormulaActiveCell
!= aFormulaAddr
)
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
)
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
);
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
);
1618 for(const auto& rAddr
: vecNew
)
1620 uno::Reference
< XAccessible
> xChild
;
1621 if (rAddr
== aFormulaAddr
)
1623 xChild
= m_pAccFormulaCell
.get();
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
) )
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
)
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() )
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();
1714 if(m_bFormulaLastMode
)
1716 nRefX
=rViewData
.GetRefEndX();
1717 nRefY
=rViewData
.GetRefEndY();
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());
1733 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */