diff options
Diffstat (limited to 'sc/source/ui/Accessibility/AccessibleSpreadsheet.cxx')
-rw-r--r-- | sc/source/ui/Accessibility/AccessibleSpreadsheet.cxx | 1667 |
1 files changed, 1667 insertions, 0 deletions
diff --git a/sc/source/ui/Accessibility/AccessibleSpreadsheet.cxx b/sc/source/ui/Accessibility/AccessibleSpreadsheet.cxx new file mode 100644 index 000000000..e435c37e7 --- /dev/null +++ b/sc/source/ui/Accessibility/AccessibleSpreadsheet.cxx @@ -0,0 +1,1667 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <AccessibleSpreadsheet.hxx> +#include <AccessibleCell.hxx> +#include <AccessibleDocument.hxx> +#include <tabvwsh.hxx> +#include <document.hxx> +#include <hints.hxx> +#include <scmod.hxx> +#include <markdata.hxx> +#include <gridwin.hxx> + +#include <o3tl/safeint.hxx> +#include <unotools/accessiblestatesethelper.hxx> +#include <unotools/accessiblerelationsethelper.hxx> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleTableModelChangeType.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <sal/log.hxx> +#include <tools/gen.hxx> +#include <svtools/colorcfg.hxx> +#include <vcl/svapp.hxx> +#include <scresid.hxx> +#include <strings.hrc> + +#include <algorithm> +#include <cstdlib> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +static bool CompMinCol(const std::pair<sal_uInt16,sal_uInt16> & pc1,const std::pair<sal_uInt16,sal_uInt16> &pc2) +{ + return pc1.first < pc2.first; +} + +ScMyAddress ScAccessibleSpreadsheet::CalcScAddressFromRangeList(ScRangeList *pMarkedRanges,sal_Int32 nSelectedChildIndex) +{ + if (pMarkedRanges->size() <= 1) + { + ScRange const & rRange = pMarkedRanges->front(); + // MT IA2: Not used. + // const int nRowNum = rRange.aEnd.Row() - rRange.aStart.Row() + 1; + const int nColNum = rRange.aEnd.Col() - rRange.aStart.Col() + 1; + const int nCurCol = nSelectedChildIndex % nColNum; + const int nCurRow = (nSelectedChildIndex - nCurCol)/nColNum; + return ScMyAddress(static_cast<SCCOL>(rRange.aStart.Col() + nCurCol), rRange.aStart.Row() + nCurRow, maActiveCell.Tab()); + } + else + { + ScDocument* pDoc= GetDocument(mpViewShell); + sal_Int32 nMinRow = pDoc->MaxRow(); + sal_Int32 nMaxRow = 0; + std::vector<ScRange> aRanges; + size_t nSize = pMarkedRanges->size(); + for (size_t i = 0; i < nSize; ++i) + { + ScRange const & rRange = (*pMarkedRanges)[i]; + if (rRange.aStart.Tab() != rRange.aEnd.Tab()) + { + if ((maActiveCell.Tab() >= rRange.aStart.Tab()) || + maActiveCell.Tab() <= rRange.aEnd.Tab()) + { + aRanges.push_back(rRange); + nMinRow = std::min(rRange.aStart.Row(),nMinRow); + nMaxRow = std::max(rRange.aEnd.Row(),nMaxRow); + } + else + SAL_WARN("sc", "Range of wrong table"); + } + else if(rRange.aStart.Tab() == maActiveCell.Tab()) + { + aRanges.push_back(rRange); + nMinRow = std::min(rRange.aStart.Row(),nMinRow); + nMaxRow = std::max(rRange.aEnd.Row(),nMaxRow); + } + else + SAL_WARN("sc", "Range of wrong table"); + } + int nCurrentIndex = 0 ; + for(sal_Int32 row = nMinRow ; row <= nMaxRow ; ++row) + { + std::vector<std::pair<SCCOL, SCCOL>> aVecCol; + for (ScRange const & r : aRanges) + { + if ( row >= r.aStart.Row() && row <= r.aEnd.Row()) + { + aVecCol.emplace_back(r.aStart.Col(), r.aEnd.Col()); + } + } + std::sort(aVecCol.begin(), aVecCol.end(), CompMinCol); + for (const std::pair<SCCOL, SCCOL> &pairCol : aVecCol) + { + SCCOL nCol = pairCol.second - pairCol.first + 1; + if (nCol + nCurrentIndex > nSelectedChildIndex) + { + return ScMyAddress(static_cast<SCCOL>(pairCol.first + nSelectedChildIndex - nCurrentIndex), row, maActiveCell.Tab()); + } + nCurrentIndex += nCol; + } + } + } + return ScMyAddress(0,0,maActiveCell.Tab()); +} + +bool ScAccessibleSpreadsheet::CalcScRangeDifferenceMax(const ScRange & rSrc, const ScRange & rDest, int nMax, + std::vector<ScMyAddress> &vecRet, int &nSize) +{ + //Src Must be :Src > Dest + if (rDest.Contains(rSrc)) + {//Here is Src In Dest,Src <= Dest + return false; + } + if (!rDest.Intersects(rSrc)) + { + int nCellCount = sal_uInt32(rDest.aEnd.Col() - rDest.aStart.Col() + 1) + * sal_uInt32(rDest.aEnd.Row() - rDest.aStart.Row() + 1) + * sal_uInt32(rDest.aEnd.Tab() - rDest.aStart.Tab() + 1); + if (nCellCount + nSize > nMax) + { + return true; + } + else if(nCellCount > 0) + { + for (sal_Int32 row = rDest.aStart.Row(); row <= rDest.aEnd.Row();++row) + { + for (sal_uInt16 col = rDest.aStart.Col(); col <= rDest.aEnd.Col();++col) + { + vecRet.emplace_back(col,row,rDest.aStart.Tab()); + } + } + } + return false; + } + sal_Int32 nMinRow = rSrc.aStart.Row(); + sal_Int32 nMaxRow = rSrc.aEnd.Row(); + for (; nMinRow <= nMaxRow ; ++nMinRow,--nMaxRow) + { + for (sal_uInt16 col = rSrc.aStart.Col(); col <= rSrc.aEnd.Col();++col) + { + if (nSize > nMax) + { + return true; + } + ScMyAddress cell(col,nMinRow,rSrc.aStart.Tab()); + if(!rDest.Contains(cell)) + {//In Src ,Not In Dest + vecRet.push_back(cell); + ++nSize; + } + } + if (nMinRow != nMaxRow) + { + for (sal_uInt16 col = rSrc.aStart.Col(); col <= rSrc.aEnd.Col();++col) + { + if (nSize > nMax) + { + return true; + } + ScMyAddress cell(col,nMaxRow,rSrc.aStart.Tab()); + if(!rDest.Contains(cell)) + {//In Src ,Not In Dest + vecRet.push_back(cell); + ++nSize; + } + } + } + } + return false; +} + +//In Src , Not in Dest +bool ScAccessibleSpreadsheet::CalcScRangeListDifferenceMax(ScRangeList *pSrc, ScRangeList *pDest, + int nMax, std::vector<ScMyAddress> &vecRet) +{ + if (pSrc == nullptr || pDest == nullptr) + { + return false; + } + int nSize =0; + if (pDest->GetCellCount() == 0)//if the Dest Rang List is empty + { + if (pSrc->GetCellCount() > o3tl::make_unsigned(nMax))//if the Src Cell count is greater than nMax + { + return true; + } + //now the cell count is less than nMax + vecRet.reserve(10); + size_t nSrcSize = pSrc->size(); + for (size_t i = 0; i < nSrcSize; ++i) + { + ScRange const & rRange = (*pSrc)[i]; + for (sal_Int32 row = rRange.aStart.Row(); row <= rRange.aEnd.Row();++row) + { + for (sal_uInt16 col = rRange.aStart.Col(); col <= rRange.aEnd.Col();++col) + { + vecRet.emplace_back(col,row, rRange.aStart.Tab()); + } + } + } + return false; + } + //the Dest Rang List is not empty + vecRet.reserve(10); + size_t nSizeSrc = pSrc->size(); + for (size_t i = 0; i < nSizeSrc; ++i) + { + ScRange const & rRange = (*pSrc)[i]; + size_t nSizeDest = pDest->size(); + for (size_t j = 0; j < nSizeDest; ++j) + { + ScRange const & rRangeDest = (*pDest)[j]; + if (CalcScRangeDifferenceMax(rRange,rRangeDest,nMax,vecRet,nSize)) + { + return true; + } + } + } + return false; +} + +//===== internal ============================================================ + +// FIXME: really unclear why we have an ScAccessibleTableBase with +// only this single sub-class +ScAccessibleSpreadsheet::ScAccessibleSpreadsheet( + ScAccessibleDocument* pAccDoc, + ScTabViewShell* pViewShell, + SCTAB nTab, + ScSplitPos eSplitPos) + : + ScAccessibleTableBase( pAccDoc, GetDocument(pViewShell), ScRange( 0, 0, nTab, GetDocument(pViewShell)->MaxCol(), GetDocument(pViewShell)->MaxRow(), nTab)), + mbIsSpreadsheet( true ), + m_bFormulaMode( false ), + m_bFormulaLastMode( false ), + m_nMinX(0),m_nMaxX(0),m_nMinY(0),m_nMaxY(0) +{ + ConstructScAccessibleSpreadsheet( pAccDoc, pViewShell, nTab, eSplitPos ); +} + +ScAccessibleSpreadsheet::ScAccessibleSpreadsheet( + ScAccessibleSpreadsheet& rParent, const ScRange& rRange ) : + ScAccessibleTableBase( rParent.mpAccDoc, rParent.mpDoc, rRange), + mbIsSpreadsheet( false ), + m_bFormulaMode( false ), + m_bFormulaLastMode( false ), + m_nMinX(0),m_nMaxX(0),m_nMinY(0),m_nMaxY(0) +{ + ConstructScAccessibleSpreadsheet( rParent.mpAccDoc, rParent.mpViewShell, rParent.mnTab, rParent.meSplitPos ); +} + +ScAccessibleSpreadsheet::~ScAccessibleSpreadsheet() +{ + mpMarkedRanges.reset(); + if (mpViewShell) + mpViewShell->RemoveAccessibilityObject(*this); +} + +void ScAccessibleSpreadsheet::ConstructScAccessibleSpreadsheet( + ScAccessibleDocument* pAccDoc, + ScTabViewShell* pViewShell, + SCTAB nTab, + ScSplitPos eSplitPos) +{ + mpViewShell = pViewShell; + mpMarkedRanges = nullptr; + mpAccDoc = pAccDoc; + mpAccCell.clear(); + meSplitPos = eSplitPos; + mnTab = nTab; + mbDelIns = false; + mbIsFocusSend = false; + if (!mpViewShell) + return; + + mpViewShell->AddAccessibilityObject(*this); + + const ScViewData& rViewData = mpViewShell->GetViewData(); + maActiveCell = rViewData.GetCurPos(); + mpAccCell = GetAccessibleCellAt(maActiveCell.Row(), maActiveCell.Col()); + ScDocument* pScDoc= GetDocument(mpViewShell); + if (pScDoc) + { + pScDoc->GetName( maActiveCell.Tab(), m_strOldTabName ); + } +} + +void SAL_CALL ScAccessibleSpreadsheet::disposing() +{ + SolarMutexGuard aGuard; + if (mpViewShell) + { + mpViewShell->RemoveAccessibilityObject(*this); + mpViewShell = nullptr; + } + mpAccCell.clear(); + + ScAccessibleTableBase::disposing(); +} + +void ScAccessibleSpreadsheet::CompleteSelectionChanged(bool bNewState) +{ + if (IsFormulaMode()) + { + return ; + } + mpMarkedRanges.reset(); + + AccessibleEventObject aEvent; + aEvent.EventId = AccessibleEventId::STATE_CHANGED; + if (bNewState) + aEvent.NewValue <<= AccessibleStateType::SELECTED; + else + aEvent.OldValue <<= AccessibleStateType::SELECTED; + aEvent.Source = uno::Reference< XAccessibleContext >(this); + + CommitChange(aEvent); +} + +void ScAccessibleSpreadsheet::LostFocus() +{ + AccessibleEventObject aEvent; + aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED; + aEvent.Source = uno::Reference< XAccessibleContext >(this); + aEvent.OldValue <<= uno::Reference<XAccessible>(mpAccCell); + + CommitChange(aEvent); + + CommitFocusLost(); +} + +void ScAccessibleSpreadsheet::GotFocus() +{ + CommitFocusGained(); + AccessibleEventObject aEvent; + aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED; + aEvent.Source = uno::Reference< XAccessibleContext >(this); + uno::Reference< XAccessible > xNew; + if (IsFormulaMode()) + { + if (!m_pAccFormulaCell.is() || !m_bFormulaLastMode) + { + ScAddress aFormulaAddr; + if(!GetFormulaCurrentFocusCell(aFormulaAddr)) + { + return; + } + m_pAccFormulaCell = GetAccessibleCellAt(aFormulaAddr.Row(),aFormulaAddr.Col()); + } + xNew = m_pAccFormulaCell.get(); + } + else + { + if(mpAccCell->GetCellAddress() == maActiveCell) + { + xNew = mpAccCell.get(); + } + else + { + CommitFocusCell(maActiveCell); + return ; + } + } + aEvent.NewValue <<= xNew; + + CommitChange(aEvent); +} + +void ScAccessibleSpreadsheet::BoundingBoxChanged() +{ + AccessibleEventObject aEvent; + aEvent.EventId = AccessibleEventId::BOUNDRECT_CHANGED; + aEvent.Source = uno::Reference< XAccessibleContext >(this); + + CommitChange(aEvent); +} + +void ScAccessibleSpreadsheet::VisAreaChanged() +{ + AccessibleEventObject aEvent; + aEvent.EventId = AccessibleEventId::VISIBLE_DATA_CHANGED; + aEvent.Source = uno::Reference< XAccessibleContext >(this); + + CommitChange(aEvent); +} + + //===== SfxListener ===================================================== + +void ScAccessibleSpreadsheet::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) +{ + if ( auto pRefHint = dynamic_cast<const ScUpdateRefHint*>(&rHint) ) + { + if (pRefHint->GetMode() == URM_INSDEL && pRefHint->GetDz() == 0) //test whether table is inserted or deleted + { + if (((pRefHint->GetRange().aStart.Col() == maRange.aStart.Col()) && + (pRefHint->GetRange().aEnd.Col() == maRange.aEnd.Col())) || + ((pRefHint->GetRange().aStart.Row() == maRange.aStart.Row()) && + (pRefHint->GetRange().aEnd.Row() == maRange.aEnd.Row()))) + { + // ignore next SfxHintId::ScDataChanged notification + mbDelIns = true; + + SCROW nFirstRow = -1; + SCROW nLastRow = -1; + SCCOL nFirstCol = -1; + SCCOL nLastCol = -1; + + sal_Int16 nId(0); + SCCOL nX(pRefHint->GetDx()); + SCROW nY(pRefHint->GetDy()); + ScRange aRange(pRefHint->GetRange()); + if ((nX < 0) || (nY < 0)) + { + assert(!((nX < 0) && (nY < 0)) && "should not be possible to remove row and column at the same time"); + + // Range in the update hint is the range after the removed rows/columns; + // calculate indices for the removed ones from that + if (nX < 0) + { + nId = AccessibleTableModelChangeType::COLUMNS_REMOVED; + nFirstCol = aRange.aStart.Col() + nX; + nLastCol = aRange.aStart.Col() - 1; + } + else + { + nId = AccessibleTableModelChangeType::ROWS_REMOVED; + nFirstRow = aRange.aStart.Row() + nY; + nLastRow = aRange.aStart.Row() - 1; + } + } + else if ((nX > 0) || (nY > 0)) + { + assert(!((nX > 0) && (nY > 0)) && "should not be possible to add row and column at the same time"); + + // Range in the update hint is from first inserted row/column to last one in spreadsheet; + // calculate indices for the inserted ones from that + if (nX > 0) + { + nId = AccessibleTableModelChangeType::COLUMNS_INSERTED; + nFirstCol = aRange.aStart.Col(); + nLastCol = aRange.aStart.Col() + nX - 1; + } + else + { + nId = AccessibleTableModelChangeType::ROWS_INSERTED; + nFirstRow = aRange.aStart.Row(); + nLastRow = aRange.aStart.Row() + nY -1; + } + } + else + { + assert(false && "is it a deletion or an insertion?"); + } + + CommitTableModelChange(nFirstRow, nFirstCol, nLastRow, nLastCol, nId); + + AccessibleEventObject aEvent; + aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED; + aEvent.Source = uno::Reference< XAccessibleContext >(this); + aEvent.NewValue <<= uno::Reference<XAccessible>(mpAccCell); + + CommitChange(aEvent); + } + } + } + else + { + if (rHint.GetId() == SfxHintId::ScAccCursorChanged) + { + if (mpViewShell) + { + ScViewData& rViewData = mpViewShell->GetViewData(); + + m_bFormulaMode = rViewData.IsRefMode() || SC_MOD()->IsFormulaMode(); + if ( m_bFormulaMode ) + { + NotifyRefMode(); + m_bFormulaLastMode = true; + return; + } + if (m_bFormulaLastMode) + {//Last Notify Mode Is Formula Mode. + m_vecFormulaLastMyAddr.clear(); + RemoveFormulaSelection(true); + m_pAccFormulaCell.clear(); + //Remove All Selection + } + m_bFormulaLastMode = m_bFormulaMode; + + AccessibleEventObject aEvent; + aEvent.Source = uno::Reference< XAccessible >(this); + ScAddress aNewCell = rViewData.GetCurPos(); + if(aNewCell.Tab() != maActiveCell.Tab()) + { + aEvent.EventId = AccessibleEventId::PAGE_CHANGED; + auto pAccParent = getAccessibleParent(); + ScAccessibleDocument *pAccDoc = + static_cast<ScAccessibleDocument*>(pAccParent.get()); + if(pAccDoc) + { + pAccDoc->CommitChange(aEvent); + } + } + bool bNewPosCell = (aNewCell != maActiveCell) || mpViewShell->GetForceFocusOnCurCell(); // #i123629# + bool bNewPosCellFocus=false; + if ( bNewPosCell && IsFocused() && aNewCell.Tab() == maActiveCell.Tab() ) + {//single Focus + bNewPosCellFocus=true; + } + ScMarkData &refScMarkData = rViewData.GetMarkData(); + // MT IA2: Not used + // int nSelCount = refScMarkData.GetSelectCount(); + bool bIsMark =refScMarkData.IsMarked(); + bool bIsMultMark = refScMarkData.IsMultiMarked(); + bool bNewMarked = refScMarkData.GetTableSelect(aNewCell.Tab()) && ( bIsMark || bIsMultMark ); +// sal_Bool bNewCellSelected = isAccessibleSelected(aNewCell.Row(), aNewCell.Col()); + sal_uInt16 nTab = rViewData.GetTabNo(); + const ScRange& aMarkRange = refScMarkData.GetMarkArea(); + aEvent.OldValue.clear(); + ScDocument* pDoc= GetDocument(mpViewShell); + //Mark All + if ( !bNewPosCellFocus && + (bNewMarked || bIsMark || bIsMultMark ) && + aMarkRange == ScRange( 0,0,nTab, pDoc->MaxCol(),pDoc->MaxRow(),nTab ) ) + { + aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_WITHIN; + aEvent.NewValue.clear(); + CommitChange(aEvent); + return ; + } + if (!mpMarkedRanges) + { + mpMarkedRanges.reset(new ScRangeList()); + } + refScMarkData.FillRangeListWithMarks(mpMarkedRanges.get(), true); + + //For Whole Col Row + bool bWholeRow = std::abs(aMarkRange.aStart.Row() - aMarkRange.aEnd.Row()) == pDoc->MaxRow() ; + bool bWholeCol = ::abs(aMarkRange.aStart.Col() - aMarkRange.aEnd.Col()) == pDoc->MaxCol() ; + if ((bNewMarked || bIsMark || bIsMultMark ) && (bWholeCol || bWholeRow)) + { + if ( aMarkRange != m_aLastWithInMarkRange ) + { + RemoveSelection(refScMarkData); + if(bNewPosCell) + { + CommitFocusCell(aNewCell); + } + bool bLastIsWholeColRow = + (std::abs(m_aLastWithInMarkRange.aStart.Row() - m_aLastWithInMarkRange.aEnd.Row()) == pDoc->MaxRow() && bWholeRow) || + (::abs(m_aLastWithInMarkRange.aStart.Col() - m_aLastWithInMarkRange.aEnd.Col()) == pDoc->MaxCol() && bWholeCol); + bool bSelSmaller= + bLastIsWholeColRow && + !aMarkRange.Contains(m_aLastWithInMarkRange) && + aMarkRange.Intersects(m_aLastWithInMarkRange); + if( !bSelSmaller ) + { + aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_WITHIN; + aEvent.NewValue.clear(); + CommitChange(aEvent); + } + m_aLastWithInMarkRange = aMarkRange; + } + return ; + } + m_aLastWithInMarkRange = aMarkRange; + int nNewMarkCount = mpMarkedRanges->GetCellCount(); + bool bSendSingle= (0 == nNewMarkCount) && bNewPosCell; + if (bSendSingle) + { + RemoveSelection(refScMarkData); + if(bNewPosCellFocus) + { + CommitFocusCell(aNewCell); + } + uno::Reference< XAccessible > xChild ; + if (bNewPosCellFocus) + { + xChild = mpAccCell.get(); + } + else + { + mpAccCell = GetAccessibleCellAt(aNewCell.Row(),aNewCell.Col()); + xChild = mpAccCell.get(); + + maActiveCell = aNewCell; + aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED_NOFOCUS; + aEvent.NewValue <<= xChild; + aEvent.OldValue <<= uno::Reference< XAccessible >(); + CommitChange(aEvent); + } + aEvent.EventId = AccessibleEventId::SELECTION_CHANGED; + aEvent.NewValue <<= xChild; + CommitChange(aEvent); + OSL_ASSERT(m_mapSelectionSend.count(aNewCell) == 0 ); + m_mapSelectionSend.emplace(aNewCell,xChild); + + } + else + { + ScRange aDelRange; + bool bIsDel = rViewData.GetDelMark( aDelRange ); + if ( (!bIsDel || aMarkRange != aDelRange) && + bNewMarked && + nNewMarkCount > 0 && + m_LastMarkedRanges != *mpMarkedRanges ) + { + RemoveSelection(refScMarkData); + if(bNewPosCellFocus) + { + CommitFocusCell(aNewCell); + } + std::vector<ScMyAddress> vecNew; + if(CalcScRangeListDifferenceMax(mpMarkedRanges.get(), &m_LastMarkedRanges,10,vecNew)) + { + aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_WITHIN; + aEvent.NewValue.clear(); + CommitChange(aEvent); + } + else + { + for(const auto& rAddr : vecNew) + { + uno::Reference< XAccessible > xChild = getAccessibleCellAt(rAddr.Row(),rAddr.Col()); + if (!(bNewPosCellFocus && rAddr == aNewCell) ) + { + aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED_NOFOCUS; + aEvent.NewValue <<= xChild; + CommitChange(aEvent); + } + aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_ADD; + aEvent.NewValue <<= xChild; + CommitChange(aEvent); + m_mapSelectionSend.emplace(rAddr,xChild); + } + } + } + } + if (bNewPosCellFocus && maActiveCell != aNewCell) + { + CommitFocusCell(aNewCell); + } + m_LastMarkedRanges = *mpMarkedRanges; + } + } + else if (rHint.GetId() == SfxHintId::ScDataChanged) + { + if (!mbDelIns) + CommitTableModelChange(maRange.aStart.Row(), maRange.aStart.Col(), maRange.aEnd.Row(), maRange.aEnd.Col(), AccessibleTableModelChangeType::UPDATE); + else + mbDelIns = false; + if (mpViewShell) + { + ScViewData& rViewData = mpViewShell->GetViewData(); + ScAddress aNewCell = rViewData.GetCurPos(); + if( maActiveCell == aNewCell) + { + ScDocument* pScDoc= GetDocument(mpViewShell); + if (pScDoc) + { + OUString valStr(pScDoc->GetString(aNewCell.Col(),aNewCell.Row(),aNewCell.Tab())); + if(m_strCurCellValue != valStr) + { + AccessibleEventObject aEvent; + aEvent.EventId = AccessibleEventId::VALUE_CHANGED; + mpAccCell->CommitChange(aEvent); + m_strCurCellValue=valStr; + } + OUString tabName; + pScDoc->GetName( maActiveCell.Tab(), tabName ); + if( m_strOldTabName != tabName ) + { + AccessibleEventObject aEvent; + aEvent.EventId = AccessibleEventId::NAME_CHANGED; + OUString sOldName(ScResId(STR_ACC_TABLE_NAME)); + sOldName = sOldName.replaceFirst("%1", m_strOldTabName); + aEvent.OldValue <<= sOldName; + OUString sNewName(ScResId(STR_ACC_TABLE_NAME)); + sNewName = sNewName.replaceFirst("%1", tabName); + aEvent.NewValue <<= sNewName; + CommitChange( aEvent ); + m_strOldTabName = tabName; + } + } + } + } + } + // commented out, because to use a ModelChangeEvent is not the right way + // at the moment there is no way, but the Java/Gnome Api should be extended sometime +/* if (mpViewShell) + { + Rectangle aNewVisCells(GetVisCells(GetVisArea(mpViewShell, meSplitPos))); + + Rectangle aNewPos(aNewVisCells); + + if (aNewVisCells.Overlaps(maVisCells)) + aNewPos.Union(maVisCells); + else + CommitTableModelChange(maVisCells.Top(), maVisCells.Left(), maVisCells.Bottom(), maVisCells.Right(), AccessibleTableModelChangeType::UPDATE); + + maVisCells = aNewVisCells; + + CommitTableModelChange(aNewPos.Top(), aNewPos.Left(), aNewPos.Bottom(), aNewPos.Right(), AccessibleTableModelChangeType::UPDATE); + } + }*/ + } + + ScAccessibleTableBase::Notify(rBC, rHint); +} + +void ScAccessibleSpreadsheet::RemoveSelection(const ScMarkData &refScMarkData) +{ + AccessibleEventObject aEvent; + aEvent.Source = uno::Reference< XAccessible >(this); + MAP_ADDR_XACC::iterator miRemove = m_mapSelectionSend.begin(); + while (miRemove != m_mapSelectionSend.end()) + { + if (refScMarkData.IsCellMarked(miRemove->first.Col(),miRemove->first.Row(),true) || + refScMarkData.IsCellMarked(miRemove->first.Col(),miRemove->first.Row()) ) + { + ++miRemove; + continue; + } + aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_REMOVE; + aEvent.NewValue <<= miRemove->second; + CommitChange(aEvent); + miRemove = m_mapSelectionSend.erase(miRemove); + } +} +void ScAccessibleSpreadsheet::CommitFocusCell(const ScAddress &aNewCell) +{ + OSL_ASSERT(!IsFormulaMode()); + if(IsFormulaMode()) + { + return ; + } + AccessibleEventObject aEvent; + aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED; + aEvent.Source = uno::Reference< XAccessible >(this); + aEvent.OldValue <<= uno::Reference<XAccessible>(mpAccCell); + mpAccCell.clear(); + mpAccCell = GetAccessibleCellAt(aNewCell.Row(), aNewCell.Col()); + aEvent.NewValue <<= uno::Reference<XAccessible>(mpAccCell); + maActiveCell = aNewCell; + ScDocument* pScDoc= GetDocument(mpViewShell); + if (pScDoc) + { + m_strCurCellValue = pScDoc->GetString(maActiveCell.Col(),maActiveCell.Row(),maActiveCell.Tab()); + } + CommitChange(aEvent); +} + +//===== XAccessibleTable ================================================ + +uno::Reference< XAccessibleTable > SAL_CALL ScAccessibleSpreadsheet::getAccessibleRowHeaders( ) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + uno::Reference< XAccessibleTable > xAccessibleTable; + if( mpDoc && mbIsSpreadsheet ) + { + if( std::optional<ScRange> oRowRange = mpDoc->GetRepeatRowRange( mnTab ) ) + { + SCROW nStart = oRowRange->aStart.Row(); + SCROW nEnd = oRowRange->aEnd.Row(); + ScDocument* pDoc = GetDocument(mpViewShell); + if( (0 <= nStart) && (nStart <= nEnd) && (nEnd <= pDoc->MaxRow()) ) + xAccessibleTable.set( new ScAccessibleSpreadsheet( *this, ScRange( 0, nStart, mnTab, pDoc->MaxCol(), nEnd, mnTab ) ) ); + } + } + return xAccessibleTable; +} + +uno::Reference< XAccessibleTable > SAL_CALL ScAccessibleSpreadsheet::getAccessibleColumnHeaders( ) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + uno::Reference< XAccessibleTable > xAccessibleTable; + if( mpDoc && mbIsSpreadsheet ) + { + if( std::optional<ScRange> oColRange = mpDoc->GetRepeatColRange( mnTab ) ) + { + SCCOL nStart = oColRange->aStart.Col(); + SCCOL nEnd = oColRange->aEnd.Col(); + ScDocument* pDoc = GetDocument(mpViewShell); + if( (0 <= nStart) && (nStart <= nEnd) && (nEnd <= pDoc->MaxCol()) ) + xAccessibleTable.set( new ScAccessibleSpreadsheet( *this, ScRange( nStart, 0, mnTab, nEnd, pDoc->MaxRow(), mnTab ) ) ); + } + } + return xAccessibleTable; +} + +uno::Sequence< sal_Int32 > SAL_CALL ScAccessibleSpreadsheet::getSelectedAccessibleRows( ) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + uno::Sequence<sal_Int32> aSequence; + if (IsFormulaMode()) + { + return aSequence; + } + if (mpViewShell) + { + aSequence.realloc(maRange.aEnd.Row() - maRange.aStart.Row() + 1); + const ScMarkData& rMarkdata = mpViewShell->GetViewData().GetMarkData(); + sal_Int32* pSequence = aSequence.getArray(); + sal_Int32 nCount(0); + for (SCROW i = maRange.aStart.Row(); i <= maRange.aEnd.Row(); ++i) + { + if (rMarkdata.IsRowMarked(i)) + { + pSequence[nCount] = i; + ++nCount; + } + } + aSequence.realloc(nCount); + } + else + aSequence.realloc(0); + return aSequence; +} + +uno::Sequence< sal_Int32 > SAL_CALL ScAccessibleSpreadsheet::getSelectedAccessibleColumns( ) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + uno::Sequence<sal_Int32> aSequence; + if (IsFormulaMode() || !mpViewShell) + return aSequence; + + aSequence.realloc(maRange.aEnd.Col() - maRange.aStart.Col() + 1); + sal_Int32* pSequence = aSequence.getArray(); + sal_Int32 nCount(0); + const ScMarkData& rMarkdata = mpViewShell->GetViewData().GetMarkData(); + for (SCCOL i = maRange.aStart.Col(); i <= maRange.aEnd.Col(); ++i) + { + if (rMarkdata.IsColumnMarked(i)) + { + pSequence[nCount] = i; + ++nCount; + } + } + aSequence.realloc(nCount); + return aSequence; +} + +sal_Bool SAL_CALL ScAccessibleSpreadsheet::isAccessibleRowSelected( sal_Int32 nRow ) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + if (IsFormulaMode()) + { + return false; + } + + if ((nRow > (maRange.aEnd.Row() - maRange.aStart.Row())) || (nRow < 0)) + throw lang::IndexOutOfBoundsException(); + + bool bResult(false); + if (mpViewShell) + { + const ScMarkData& rMarkdata = mpViewShell->GetViewData().GetMarkData(); + bResult = rMarkdata.IsRowMarked(static_cast<SCROW>(nRow)); + } + return bResult; +} + +sal_Bool SAL_CALL ScAccessibleSpreadsheet::isAccessibleColumnSelected( sal_Int32 nColumn ) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + + if (IsFormulaMode()) + { + return false; + } + if ((nColumn > (maRange.aEnd.Col() - maRange.aStart.Col())) || (nColumn < 0)) + throw lang::IndexOutOfBoundsException(); + + bool bResult(false); + if (mpViewShell) + { + const ScMarkData& rMarkdata = mpViewShell->GetViewData().GetMarkData(); + bResult = rMarkdata.IsColumnMarked(static_cast<SCCOL>(nColumn)); + } + return bResult; +} + +rtl::Reference<ScAccessibleCell> ScAccessibleSpreadsheet::GetAccessibleCellAt(sal_Int32 nRow, sal_Int32 nColumn) +{ + if (IsFormulaMode()) + { + ScAddress aCellAddress(static_cast<SCCOL>(nColumn), nRow, mpViewShell->GetViewData().GetTabNo()); + if ((aCellAddress == m_aFormulaActiveCell) && m_pAccFormulaCell.is()) + { + return m_pAccFormulaCell; + } + else + return ScAccessibleCell::create(this, mpViewShell, aCellAddress, GetAccessibleIndexFormula(nRow, nColumn), meSplitPos, mpAccDoc); + } + else + { + ScAddress aCellAddress(static_cast<SCCOL>(maRange.aStart.Col() + nColumn), + static_cast<SCROW>(maRange.aStart.Row() + nRow), maRange.aStart.Tab()); + if ((aCellAddress == maActiveCell) && mpAccCell.is()) + { + return mpAccCell; + } + else + return ScAccessibleCell::create(this, mpViewShell, aCellAddress, getAccessibleIndex(nRow, nColumn), meSplitPos, mpAccDoc); + } +} + +uno::Reference< XAccessible > SAL_CALL ScAccessibleSpreadsheet::getAccessibleCellAt( sal_Int32 nRow, sal_Int32 nColumn ) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + if (!IsFormulaMode()) + { + if (nRow > (maRange.aEnd.Row() - maRange.aStart.Row()) || + nRow < 0 || + nColumn > (maRange.aEnd.Col() - maRange.aStart.Col()) || + nColumn < 0) + throw lang::IndexOutOfBoundsException(); + } + rtl::Reference<ScAccessibleCell> pAccessibleCell = GetAccessibleCellAt(nRow, nColumn); + return pAccessibleCell; +} + +sal_Bool SAL_CALL ScAccessibleSpreadsheet::isAccessibleSelected( sal_Int32 nRow, sal_Int32 nColumn ) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + + if (IsFormulaMode()) + { + ScAddress addr(static_cast<SCCOL>(nColumn), nRow, 0); + return IsScAddrFormulaSel(addr); + } + if ((nColumn > (maRange.aEnd.Col() - maRange.aStart.Col())) || (nColumn < 0) || + (nRow > (maRange.aEnd.Row() - maRange.aStart.Row())) || (nRow < 0)) + throw lang::IndexOutOfBoundsException(); + + bool bResult(false); + if (mpViewShell) + { + const ScMarkData& rMarkdata = mpViewShell->GetViewData().GetMarkData(); + bResult = rMarkdata.IsCellMarked(static_cast<SCCOL>(nColumn), static_cast<SCROW>(nRow)); + } + return bResult; +} + + //===== XAccessibleComponent ============================================ + +uno::Reference< XAccessible > SAL_CALL ScAccessibleSpreadsheet::getAccessibleAtPoint(const awt::Point& rPoint) +{ + uno::Reference< XAccessible > xAccessible; + if (containsPoint(rPoint)) + { + SolarMutexGuard aGuard; + IsObjectValid(); + if (mpViewShell) + { + SCCOL nX; + SCROW nY; + mpViewShell->GetViewData().GetPosFromPixel( rPoint.X, rPoint.Y, meSplitPos, nX, nY); + try { + xAccessible = getAccessibleCellAt(nY, nX); + } + catch(const css::lang::IndexOutOfBoundsException &) + { + return nullptr; + } + } + } + return xAccessible; +} + +void SAL_CALL ScAccessibleSpreadsheet::grabFocus( ) +{ + if (getAccessibleParent().is()) + { + uno::Reference<XAccessibleComponent> xAccessibleComponent(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY); + if (xAccessibleComponent.is()) + xAccessibleComponent->grabFocus(); + } +} + +sal_Int32 SAL_CALL ScAccessibleSpreadsheet::getForeground( ) +{ + return sal_Int32(COL_BLACK); +} + +sal_Int32 SAL_CALL ScAccessibleSpreadsheet::getBackground( ) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + return sal_Int32(SC_MOD()->GetColorConfig().GetColorValue( ::svtools::DOCCOLOR ).nColor); +} + + //===== XAccessibleContext ============================================== + +uno::Reference<XAccessibleRelationSet> SAL_CALL ScAccessibleSpreadsheet::getAccessibleRelationSet() +{ + rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSet; + if(mpAccDoc) + pRelationSet = mpAccDoc->GetRelationSet(nullptr); + if (pRelationSet) + return pRelationSet; + return new utl::AccessibleRelationSetHelper(); +} + +uno::Reference<XAccessibleStateSet> SAL_CALL + ScAccessibleSpreadsheet::getAccessibleStateSet() +{ + SolarMutexGuard aGuard; + uno::Reference<XAccessibleStateSet> xParentStates; + if (getAccessibleParent().is()) + { + uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext(); + xParentStates = xParentContext->getAccessibleStateSet(); + } + rtl::Reference<utl::AccessibleStateSetHelper> pStateSet = new utl::AccessibleStateSetHelper(); + if (IsDefunc(xParentStates)) + pStateSet->AddState(AccessibleStateType::DEFUNC); + else + { + pStateSet->AddState(AccessibleStateType::MANAGES_DESCENDANTS); + if (IsEditable()) + pStateSet->AddState(AccessibleStateType::EDITABLE); + pStateSet->AddState(AccessibleStateType::ENABLED); + pStateSet->AddState(AccessibleStateType::FOCUSABLE); + if (IsFocused()) + pStateSet->AddState(AccessibleStateType::FOCUSED); + pStateSet->AddState(AccessibleStateType::MULTI_SELECTABLE); + pStateSet->AddState(AccessibleStateType::OPAQUE); + pStateSet->AddState(AccessibleStateType::SELECTABLE); + if (IsCompleteSheetSelected()) + pStateSet->AddState(AccessibleStateType::SELECTED); + if (isShowing()) + pStateSet->AddState(AccessibleStateType::SHOWING); + if (isVisible()) + pStateSet->AddState(AccessibleStateType::VISIBLE); + } + return pStateSet; +} + + ///===== XAccessibleSelection =========================================== + +void SAL_CALL ScAccessibleSpreadsheet::selectAccessibleChild( sal_Int32 nChildIndex ) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + if (nChildIndex < 0 || nChildIndex >= getAccessibleChildCount()) + throw lang::IndexOutOfBoundsException(); + + if (mpViewShell) + { + sal_Int32 nCol(getAccessibleColumn(nChildIndex)); + sal_Int32 nRow(getAccessibleRow(nChildIndex)); + + SelectCell(nRow, nCol, false); + } +} + +void SAL_CALL + ScAccessibleSpreadsheet::clearAccessibleSelection( ) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + if (mpViewShell && !IsFormulaMode()) + mpViewShell->Unmark(); +} + +void SAL_CALL ScAccessibleSpreadsheet::selectAllAccessibleChildren( ) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + if (!mpViewShell) + return; + + if (IsFormulaMode()) + { + ScDocument* pDoc = GetDocument(mpViewShell); + ScViewData& rViewData = mpViewShell->GetViewData(); + mpViewShell->InitRefMode( 0, 0, rViewData.GetTabNo(), SC_REFTYPE_REF ); + rViewData.SetRefStart(0, 0, rViewData.GetTabNo()); + rViewData.SetRefEnd(pDoc->MaxCol(), pDoc->MaxRow(), rViewData.GetTabNo()); + mpViewShell->UpdateRef(pDoc->MaxCol(), pDoc->MaxRow(), rViewData.GetTabNo()); + } + else + mpViewShell->SelectAll(); +} + +sal_Int32 SAL_CALL + ScAccessibleSpreadsheet::getSelectedAccessibleChildCount( ) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + sal_Int32 nResult(0); + if (mpViewShell) + { + if (IsFormulaMode()) + { + nResult = GetRowAll() * GetColAll() ; + } + else + { + if (!mpMarkedRanges) + { + mpMarkedRanges.reset(new ScRangeList()); + ScMarkData aMarkData(mpViewShell->GetViewData().GetMarkData()); + aMarkData.FillRangeListWithMarks(mpMarkedRanges.get(), false); + } + // is possible, because there shouldn't be overlapped ranges in it + if (mpMarkedRanges) + nResult = mpMarkedRanges->GetCellCount(); + } + } + return nResult; +} + +uno::Reference<XAccessible > SAL_CALL + ScAccessibleSpreadsheet::getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex ) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + uno::Reference < XAccessible > xAccessible; + if (IsFormulaMode()) + { + if(CheckChildIndex(nSelectedChildIndex)) + { + ScAddress addr = GetChildIndexAddress(nSelectedChildIndex); + xAccessible = getAccessibleCellAt(addr.Row(), addr.Col()); + } + return xAccessible; + } + if (mpViewShell) + { + if (!mpMarkedRanges) + { + mpMarkedRanges.reset(new ScRangeList()); + mpViewShell->GetViewData().GetMarkData().FillRangeListWithMarks(mpMarkedRanges.get(), false); + } + if (mpMarkedRanges) + { + if ((nSelectedChildIndex < 0) || + (mpMarkedRanges->GetCellCount() <= o3tl::make_unsigned(nSelectedChildIndex))) + { + throw lang::IndexOutOfBoundsException(); + } + ScMyAddress addr = CalcScAddressFromRangeList(mpMarkedRanges.get(),nSelectedChildIndex); + if( m_mapSelectionSend.find(addr) != m_mapSelectionSend.end() ) + xAccessible = m_mapSelectionSend[addr]; + else + xAccessible = getAccessibleCellAt(addr.Row(), addr.Col()); + } + } + return xAccessible; +} + +void SAL_CALL ScAccessibleSpreadsheet::deselectAccessibleChild( sal_Int32 nChildIndex ) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + + if (nChildIndex < 0 || nChildIndex >= getAccessibleChildCount()) + throw lang::IndexOutOfBoundsException(); + + if (!mpViewShell) + return; + + sal_Int32 nCol(getAccessibleColumn(nChildIndex)); + sal_Int32 nRow(getAccessibleRow(nChildIndex)); + + if (IsFormulaMode()) + { + if(IsScAddrFormulaSel( + ScAddress(static_cast<SCCOL>(nCol), nRow,mpViewShell->GetViewData().GetTabNo())) + ) + { + SelectCell(nRow, nCol, true); + } + return ; + } + if (mpViewShell->GetViewData().GetMarkData().IsCellMarked(static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow))) + SelectCell(nRow, nCol, true); +} + +void ScAccessibleSpreadsheet::SelectCell(sal_Int32 nRow, sal_Int32 nCol, bool bDeselect) +{ + if (IsFormulaMode()) + { + if (bDeselect) + {//?? + return; + } + else + { + ScViewData& rViewData = mpViewShell->GetViewData(); + + mpViewShell->InitRefMode( static_cast<SCCOL>(nCol), nRow, rViewData.GetTabNo(), SC_REFTYPE_REF ); + mpViewShell->UpdateRef(static_cast<SCCOL>(nCol), nRow, rViewData.GetTabNo()); + } + return ; + } + mpViewShell->SetTabNo( maRange.aStart.Tab() ); + + mpViewShell->DoneBlockMode( true ); // continue selecting + mpViewShell->InitBlockMode( static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow), maRange.aStart.Tab(), bDeselect ); + + mpViewShell->SelectionChanged(); +} + +/* +void ScAccessibleSpreadsheet::CreateSortedMarkedCells() +{ + mpSortedMarkedCells = new std::vector<ScMyAddress>(); + mpSortedMarkedCells->reserve(mpMarkedRanges->GetCellCount()); + for ( size_t i = 0, ListSize = mpMarkedRanges->size(); i < ListSize; ++i ) + { + ScRange* pRange = (*mpMarkedRanges)[i]; + if (pRange->aStart.Tab() != pRange->aEnd.Tab()) + { + if ((maActiveCell.Tab() >= pRange->aStart.Tab()) || + maActiveCell.Tab() <= pRange->aEnd.Tab()) + { + ScRange aRange(*pRange); + aRange.aStart.SetTab(maActiveCell.Tab()); + aRange.aEnd.SetTab(maActiveCell.Tab()); + AddMarkedRange(aRange); + } + else + { + OSL_FAIL("Range of wrong table"); + } + } + else if(pRange->aStart.Tab() == maActiveCell.Tab()) + AddMarkedRange(*pRange); + else + { + OSL_FAIL("Range of wrong table"); + } + } + std::sort(mpSortedMarkedCells->begin(), mpSortedMarkedCells->end()); +} + +void ScAccessibleSpreadsheet::AddMarkedRange(const ScRange& rRange) +{ + for (SCROW nRow = rRange.aStart.Row(); nRow <= rRange.aEnd.Row(); ++nRow) + { + for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol) + { + ScMyAddress aCell(nCol, nRow, maActiveCell.Tab()); + mpSortedMarkedCells->push_back(aCell); + } + } +}*/ + + //===== XServiceInfo ==================================================== + +OUString SAL_CALL ScAccessibleSpreadsheet::getImplementationName() +{ + return "ScAccessibleSpreadsheet"; +} + +uno::Sequence< OUString> SAL_CALL + ScAccessibleSpreadsheet::getSupportedServiceNames() +{ + const css::uno::Sequence<OUString> vals { "com.sun.star.AccessibleSpreadsheet" }; + return comphelper::concatSequences(ScAccessibleContextBase::getSupportedServiceNames(), vals); +} + +//===== XTypeProvider ======================================================= + +uno::Sequence<sal_Int8> SAL_CALL + ScAccessibleSpreadsheet::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +///===== XAccessibleEventBroadcaster ===================================== + +void SAL_CALL ScAccessibleSpreadsheet::addAccessibleEventListener(const uno::Reference<XAccessibleEventListener>& xListener) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + ScAccessibleTableBase::addAccessibleEventListener(xListener); + +} + +//==== internal ========================================================= + +tools::Rectangle ScAccessibleSpreadsheet::GetBoundingBoxOnScreen() const +{ + tools::Rectangle aRect; + if (mpViewShell) + { + vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos); + if (pWindow) + aRect = pWindow->GetWindowExtentsRelative(nullptr); + } + return aRect; +} + +tools::Rectangle ScAccessibleSpreadsheet::GetBoundingBox() const +{ + tools::Rectangle aRect; + if (mpViewShell) + { + vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos); + if (pWindow) + //#101986#; extends to the same window, because the parent is the document and it has the same window + aRect = pWindow->GetWindowExtentsRelative(pWindow); + } + return aRect; +} + +bool ScAccessibleSpreadsheet::IsDefunc( + const uno::Reference<XAccessibleStateSet>& rxParentStates) +{ + return ScAccessibleContextBase::IsDefunc() || (mpViewShell == nullptr) || !getAccessibleParent().is() || + (rxParentStates.is() && rxParentStates->contains(AccessibleStateType::DEFUNC)); +} + +bool ScAccessibleSpreadsheet::IsEditable() +{ + if (IsFormulaMode()) + { + return false; + } + bool bProtected(false); + if (mpDoc && mpDoc->IsTabProtected(maRange.aStart.Tab())) + bProtected = true; + return !bProtected; +} + +bool ScAccessibleSpreadsheet::IsFocused() +{ + bool bFocused(false); + if (mpViewShell) + { + if (mpViewShell->GetViewData().GetActivePart() == meSplitPos) + bFocused = mpViewShell->GetActiveWin()->HasFocus(); + } + return bFocused; +} + +bool ScAccessibleSpreadsheet::IsCompleteSheetSelected() +{ + if (IsFormulaMode()) + { + return false; + } + + bool bResult(false); + if(mpViewShell) + { + //#103800#; use a copy of MarkData + ScMarkData aMarkData(mpViewShell->GetViewData().GetMarkData()); + if (aMarkData.IsAllMarked(maRange)) + bResult = true; + } + return bResult; +} + +ScDocument* ScAccessibleSpreadsheet::GetDocument(ScTabViewShell* pViewShell) +{ + ScDocument* pDoc = nullptr; + if (pViewShell) + pDoc = &pViewShell->GetViewData().GetDocument(); + return pDoc; +} + +sal_Bool SAL_CALL ScAccessibleSpreadsheet::selectRow( sal_Int32 row ) +{ + SolarMutexGuard g; + + if (IsFormulaMode()) + { + return false; + } + + ScDocument* pDoc = GetDocument(mpViewShell); + mpViewShell->SetTabNo( maRange.aStart.Tab() ); + mpViewShell->DoneBlockMode( true ); // continue selecting + mpViewShell->InitBlockMode( 0, row, maRange.aStart.Tab(), false, false, true ); + mpViewShell->MarkCursor( pDoc->MaxCol(), row, maRange.aStart.Tab(), false, true ); + mpViewShell->SelectionChanged(); + return true; +} + +sal_Bool SAL_CALL ScAccessibleSpreadsheet::selectColumn( sal_Int32 column ) +{ + SolarMutexGuard g; + + if (IsFormulaMode()) + { + return false; + } + + ScDocument* pDoc = GetDocument(mpViewShell); + mpViewShell->SetTabNo( maRange.aStart.Tab() ); + mpViewShell->DoneBlockMode( true ); // continue selecting + mpViewShell->InitBlockMode( static_cast<SCCOL>(column), 0, maRange.aStart.Tab(), false, true ); + mpViewShell->MarkCursor( static_cast<SCCOL>(column), pDoc->MaxRow(), maRange.aStart.Tab(), true ); + mpViewShell->SelectionChanged(); + return true; +} + +sal_Bool SAL_CALL ScAccessibleSpreadsheet::unselectRow( sal_Int32 row ) +{ + SolarMutexGuard g; + + if (IsFormulaMode()) + { + return false; + } + + ScDocument* pDoc = GetDocument(mpViewShell); + mpViewShell->SetTabNo( maRange.aStart.Tab() ); + mpViewShell->DoneBlockMode( true ); // continue selecting + mpViewShell->InitBlockMode( 0, row, maRange.aStart.Tab(), false, false, true, true ); + mpViewShell->MarkCursor( pDoc->MaxCol(), row, maRange.aStart.Tab(), false, true ); + mpViewShell->SelectionChanged(); + mpViewShell->DoneBlockMode( true ); + return true; +} + +sal_Bool SAL_CALL ScAccessibleSpreadsheet::unselectColumn( sal_Int32 column ) +{ + SolarMutexGuard g; + + if (IsFormulaMode()) + { + return false; + } + + ScDocument* pDoc = GetDocument(mpViewShell); + mpViewShell->SetTabNo( maRange.aStart.Tab() ); + mpViewShell->DoneBlockMode( true ); // continue selecting + mpViewShell->InitBlockMode( static_cast<SCCOL>(column), 0, maRange.aStart.Tab(), false, true, false, true ); + mpViewShell->MarkCursor( static_cast<SCCOL>(column), pDoc->MaxRow(), maRange.aStart.Tab(), true ); + mpViewShell->SelectionChanged(); + mpViewShell->DoneBlockMode( true ); + return true; +} + +void ScAccessibleSpreadsheet::FireFirstCellFocus() +{ + if (IsFormulaMode()) + { + return ; + } + if (mbIsFocusSend) + { + return ; + } + mbIsFocusSend = true; + AccessibleEventObject aEvent; + aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED; + aEvent.Source = uno::Reference< XAccessible >(this); + aEvent.NewValue <<= getAccessibleCellAt(maActiveCell.Row(), maActiveCell.Col()); + CommitChange(aEvent); +} + +void ScAccessibleSpreadsheet::NotifyRefMode() +{ + ScViewData& rViewData = mpViewShell->GetViewData(); + if (!rViewData.IsRefMode()) + // Not in reference mode. Bail out. + return; + + sal_uInt16 nRefStartX = rViewData.GetRefStartX(); + sal_Int32 nRefStartY = rViewData.GetRefStartY(); + sal_uInt16 nRefEndX = rViewData.GetRefEndX(); + sal_Int32 nRefEndY = rViewData.GetRefEndY(); + ScAddress aFormulaAddr; + if(!GetFormulaCurrentFocusCell(aFormulaAddr)) + { + return ; + } + if (m_aFormulaActiveCell != aFormulaAddr) + {//New Focus + m_nMinX =std::min(nRefStartX,nRefEndX); + m_nMaxX =std::max(nRefStartX,nRefEndX); + m_nMinY = std::min(nRefStartY,nRefEndY); + m_nMaxY = std::max(nRefStartY,nRefEndY); + RemoveFormulaSelection(); + AccessibleEventObject aEvent; + aEvent.Source = uno::Reference< XAccessible >(this); + aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED; + aEvent.OldValue <<= uno::Reference<XAccessible>(m_pAccFormulaCell); + m_pAccFormulaCell = GetAccessibleCellAt(aFormulaAddr.Row(), aFormulaAddr.Col()); + uno::Reference< XAccessible > xNew = m_pAccFormulaCell; + aEvent.NewValue <<= xNew; + CommitChange(aEvent); + if (nRefStartX == nRefEndX && nRefStartY == nRefEndY) + {//Selection Single + aEvent.EventId = AccessibleEventId::SELECTION_CHANGED; + aEvent.NewValue <<= xNew; + CommitChange(aEvent); + m_mapFormulaSelectionSend.emplace(aFormulaAddr,xNew); + m_vecFormulaLastMyAddr.clear(); + m_vecFormulaLastMyAddr.emplace_back(aFormulaAddr); + } + else + { + std::vector<ScMyAddress> vecCurSel; + int nCurSize = (m_nMaxX - m_nMinX +1)*(m_nMaxY - m_nMinY +1) ; + vecCurSel.reserve(nCurSize); + for (sal_uInt16 x = m_nMinX ; x <= m_nMaxX ; ++x) + { + for (sal_Int32 y = m_nMinY ; y <= m_nMaxY ; ++y) + { + ScMyAddress aAddr(x,y,0); + vecCurSel.push_back(aAddr); + } + } + std::sort(vecCurSel.begin(), vecCurSel.end()); + std::vector<ScMyAddress> vecNew; + std::set_difference(vecCurSel.begin(),vecCurSel.end(), + m_vecFormulaLastMyAddr.begin(),m_vecFormulaLastMyAddr.end(), + std::back_insert_iterator(vecNew)); + int nNewSize = vecNew.size(); + if ( nNewSize > 10 ) + { + aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_WITHIN; + aEvent.NewValue.clear(); + CommitChange(aEvent); + } + else + { + for(const auto& rAddr : vecNew) + { + uno::Reference< XAccessible > xChild; + if (rAddr == aFormulaAddr) + { + xChild = m_pAccFormulaCell.get(); + } + else + { + xChild = getAccessibleCellAt(rAddr.Row(),rAddr.Col()); + aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED_NOFOCUS; + aEvent.NewValue <<= xChild; + CommitChange(aEvent); + } + aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_ADD; + aEvent.NewValue <<= xChild; + CommitChange(aEvent); + m_mapFormulaSelectionSend.emplace(rAddr,xChild); + } + } + m_vecFormulaLastMyAddr.swap(vecCurSel); + } + } + m_aFormulaActiveCell = aFormulaAddr; +} + +void ScAccessibleSpreadsheet::RemoveFormulaSelection(bool bRemoveAll ) +{ + AccessibleEventObject aEvent; + aEvent.Source = uno::Reference< XAccessible >(this); + MAP_ADDR_XACC::iterator miRemove = m_mapFormulaSelectionSend.begin(); + while (miRemove != m_mapFormulaSelectionSend.end()) + { + if( !bRemoveAll && IsScAddrFormulaSel(miRemove->first) ) + { + ++miRemove; + continue; + } + aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_REMOVE; + aEvent.NewValue <<= miRemove->second; + CommitChange(aEvent); + miRemove = m_mapFormulaSelectionSend.erase(miRemove); + } +} + +bool ScAccessibleSpreadsheet::IsScAddrFormulaSel(const ScAddress &addr) const +{ + return addr.Col() >= m_nMinX && addr.Col() <= m_nMaxX && + addr.Row() >= m_nMinY && addr.Row() <= m_nMaxY && + addr.Tab() == mpViewShell->GetViewData().GetTabNo(); +} + +bool ScAccessibleSpreadsheet::CheckChildIndex(sal_Int32 nIndex) const +{ + sal_Int32 nMaxIndex = (m_nMaxX - m_nMinX +1)*(m_nMaxY - m_nMinY +1) -1 ; + return nIndex <= nMaxIndex && nIndex >= 0 ; +} + +ScAddress ScAccessibleSpreadsheet::GetChildIndexAddress(sal_Int32 nIndex) const +{ + sal_Int32 nRowAll = GetRowAll(); + sal_uInt16 nColAll = GetColAll(); + if (nIndex < 0 || nIndex >= nRowAll * nColAll ) + { + return ScAddress(); + } + return ScAddress( + static_cast<SCCOL>((nIndex - nIndex % nRowAll) / nRowAll + + m_nMinX), + nIndex % nRowAll + m_nMinY, + mpViewShell->GetViewData().GetTabNo() + ); +} + +sal_Int32 ScAccessibleSpreadsheet::GetAccessibleIndexFormula( sal_Int32 nRow, sal_Int32 nColumn ) +{ + sal_uInt16 nColRelative = sal_uInt16(nColumn) - GetColAll(); + sal_Int32 nRowRelative = nRow - GetRowAll(); + if (nRow < 0 || nColumn < 0 || nRowRelative >= GetRowAll() || nColRelative >= GetColAll() ) + { + return -1; + } + return GetRowAll() * nRowRelative + nColRelative; +} + +bool ScAccessibleSpreadsheet::IsFormulaMode() +{ + ScViewData& rViewData = mpViewShell->GetViewData(); + m_bFormulaMode = rViewData.IsRefMode() || SC_MOD()->IsFormulaMode(); + return m_bFormulaMode ; +} + +bool ScAccessibleSpreadsheet::GetFormulaCurrentFocusCell(ScAddress &addr) +{ + ScViewData& rViewData = mpViewShell->GetViewData(); + sal_uInt16 nRefX=0; + sal_Int32 nRefY=0; + if(m_bFormulaLastMode) + { + nRefX=rViewData.GetRefEndX(); + nRefY=rViewData.GetRefEndY(); + } + else + { + nRefX=rViewData.GetRefStartX(); + nRefY=rViewData.GetRefStartY(); + } + ScDocument* pDoc = GetDocument(mpViewShell); + if( /* Always true: nRefX >= 0 && */ nRefX <= pDoc->MaxCol() && nRefY >= 0 && nRefY <= pDoc->MaxRow()) + { + addr = ScAddress(nRefX,nRefY,rViewData.GetTabNo()); + return true; + } + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |