/src/libreoffice/sc/source/ui/Accessibility/AccessibleCell.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
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/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
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 . |
18 | | */ |
19 | | |
20 | | #include <memory> |
21 | | #include <string_view> |
22 | | |
23 | | #include <sal/config.h> |
24 | | |
25 | | #include <AccessibleCell.hxx> |
26 | | #include <scitems.hxx> |
27 | | #include <AccessibleText.hxx> |
28 | | #include <AccessibleDocument.hxx> |
29 | | #include <tabvwsh.hxx> |
30 | | #include <comphelper/sequence.hxx> |
31 | | #include <document.hxx> |
32 | | #include <attrib.hxx> |
33 | | #include <editsrc.hxx> |
34 | | #include <dociter.hxx> |
35 | | #include <markdata.hxx> |
36 | | #include <cellvalue.hxx> |
37 | | #include <formulaiter.hxx> |
38 | | #include <validat.hxx> |
39 | | |
40 | | #include <unotools/accessiblerelationsethelper.hxx> |
41 | | #include <com/sun/star/accessibility/AccessibleStateType.hpp> |
42 | | #include <com/sun/star/accessibility/AccessibleRelationType.hpp> |
43 | | #include <com/sun/star/accessibility/XAccessibleTable.hpp> |
44 | | #include <editeng/brushitem.hxx> |
45 | | #include <vcl/accessibility/AccessibleAttribute.hxx> |
46 | | #include <vcl/svapp.hxx> |
47 | | |
48 | | #include <AccessibleSpreadsheet.hxx> |
49 | | |
50 | | using namespace ::com::sun::star; |
51 | | using namespace ::com::sun::star::accessibility; |
52 | | |
53 | | rtl::Reference<ScAccessibleCell> |
54 | | ScAccessibleCell::create(const rtl::Reference<comphelper::OAccessible>& rpParent, |
55 | | ScTabViewShell* pViewShell, const ScAddress& rCellAddress, |
56 | | sal_Int64 nIndex, ScSplitPos eSplitPos, ScAccessibleDocument* pAccDoc) |
57 | 0 | { |
58 | 0 | rtl::Reference<ScAccessibleCell> x( |
59 | 0 | new ScAccessibleCell(rpParent, pViewShell, rCellAddress, nIndex, eSplitPos, pAccDoc)); |
60 | 0 | x->Init(); |
61 | 0 | return x; |
62 | 0 | } |
63 | | |
64 | | ScAccessibleCell::ScAccessibleCell(const rtl::Reference<comphelper::OAccessible>& rpParent, |
65 | | ScTabViewShell* pViewShell, const ScAddress& rCellAddress, |
66 | | sal_Int64 nIndex, ScSplitPos eSplitPos, |
67 | | ScAccessibleDocument* pAccDoc) |
68 | | : |
69 | 0 | ScAccessibleCellBase(rpParent, GetDocument(pViewShell), rCellAddress, nIndex), |
70 | 0 | ::accessibility::AccessibleStaticTextBase(CreateEditSource(pViewShell, rCellAddress, eSplitPos)), |
71 | 0 | mpViewShell(pViewShell), |
72 | 0 | mpAccDoc(pAccDoc), |
73 | 0 | meSplitPos(eSplitPos) |
74 | 0 | { |
75 | 0 | if (pViewShell) |
76 | 0 | pViewShell->AddAccessibilityObject(*this); |
77 | 0 | } |
78 | | |
79 | | ScAccessibleCell::~ScAccessibleCell() |
80 | 0 | { |
81 | 0 | if (!ScAccessibleContextBase::IsDefunc() && !rBHelper.bInDispose) |
82 | 0 | { |
83 | | // increment refcount to prevent double call of dtor |
84 | 0 | osl_atomic_increment( &m_refCount ); |
85 | | // call dispose to inform object which have a weak reference to this object |
86 | 0 | dispose(); |
87 | 0 | } |
88 | 0 | } |
89 | | |
90 | | void SAL_CALL ScAccessibleCell::disposing() |
91 | 0 | { |
92 | 0 | SolarMutexGuard aGuard; |
93 | | // dispose in AccessibleStaticTextBase |
94 | 0 | Dispose(); |
95 | |
|
96 | 0 | if (mpViewShell) |
97 | 0 | { |
98 | 0 | mpViewShell->RemoveAccessibilityObject(*this); |
99 | 0 | mpViewShell = nullptr; |
100 | 0 | } |
101 | 0 | mpAccDoc = nullptr; |
102 | |
|
103 | 0 | ScAccessibleCellBase::disposing(); |
104 | 0 | } |
105 | | |
106 | | //===== XInterface ===================================================== |
107 | | |
108 | | IMPLEMENT_FORWARD_XINTERFACE2(ScAccessibleCell, ScAccessibleCellBase, AccessibleStaticTextBase) |
109 | | |
110 | | //===== XTypeProvider =================================================== |
111 | | |
112 | | css::uno::Sequence< css::uno::Type > SAL_CALL ScAccessibleCell::getTypes() |
113 | 0 | { |
114 | 0 | return ::comphelper::concatSequences(ScAccessibleCellBase::getTypes(), |
115 | 0 | AccessibleStaticTextBase::getTypes()); |
116 | 0 | } |
117 | | IMPLEMENT_GET_IMPLEMENTATION_ID( ScAccessibleCell ) |
118 | | |
119 | | //===== XAccessibleComponent ============================================ |
120 | | |
121 | | uno::Reference< XAccessible > SAL_CALL ScAccessibleCell::getAccessibleAtPoint( |
122 | | const awt::Point& rPoint ) |
123 | 0 | { |
124 | 0 | return AccessibleStaticTextBase::getAccessibleAtPoint(rPoint); |
125 | 0 | } |
126 | | |
127 | | void SAL_CALL ScAccessibleCell::grabFocus( ) |
128 | 0 | { |
129 | 0 | SolarMutexGuard aGuard; |
130 | 0 | ensureAlive(); |
131 | 0 | if (getAccessibleParent().is() && mpViewShell) |
132 | 0 | { |
133 | 0 | uno::Reference<XAccessibleComponent> xAccessibleComponent(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY); |
134 | 0 | if (xAccessibleComponent.is()) |
135 | 0 | { |
136 | 0 | xAccessibleComponent->grabFocus(); |
137 | 0 | mpViewShell->SetCursor(maCellAddress.Col(), maCellAddress.Row()); |
138 | 0 | } |
139 | 0 | } |
140 | 0 | } |
141 | | |
142 | | AbsoluteScreenPixelRectangle ScAccessibleCell::GetBoundingBoxOnScreen() |
143 | 0 | { |
144 | 0 | AbsoluteScreenPixelRectangle aCellRect(GetBoundingBox()); |
145 | 0 | if (mpViewShell) |
146 | 0 | { |
147 | 0 | vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos); |
148 | 0 | if (pWindow) |
149 | 0 | { |
150 | 0 | AbsoluteScreenPixelRectangle aRect = pWindow->GetWindowExtentsAbsolute(); |
151 | 0 | aCellRect.Move(aRect.Left(), aRect.Top()); |
152 | 0 | } |
153 | 0 | } |
154 | 0 | return aCellRect; |
155 | 0 | } |
156 | | |
157 | | tools::Rectangle ScAccessibleCell::GetBoundingBox() |
158 | 0 | { |
159 | 0 | tools::Rectangle aCellRect; |
160 | 0 | if (mpViewShell) |
161 | 0 | { |
162 | 0 | tools::Long nSizeX, nSizeY; |
163 | 0 | mpViewShell->GetViewData().GetMergeSizePixel( |
164 | 0 | maCellAddress.Col(), maCellAddress.Row(), nSizeX, nSizeY); |
165 | 0 | aCellRect.SetSize(Size(nSizeX, nSizeY)); |
166 | 0 | aCellRect.SetPos(mpViewShell->GetViewData().GetScrPos(maCellAddress.Col(), maCellAddress.Row(), meSplitPos, true)); |
167 | |
|
168 | 0 | vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos); |
169 | 0 | if (pWindow) |
170 | 0 | { |
171 | 0 | tools::Rectangle aRect(pWindow->GetWindowExtentsRelative(*pWindow->GetAccessibleParentWindow())); |
172 | 0 | aRect.Move(-aRect.Left(), -aRect.Top()); |
173 | 0 | aCellRect = aRect.Intersection(aCellRect); |
174 | 0 | } |
175 | | |
176 | | /* #i19430# Gnopernicus reads text partly if it sticks out of the cell |
177 | | boundaries. This leads to wrong results in cases where the cell |
178 | | text is rotated, because rotation is not taken into account when |
179 | | calculating the visible part of the text. In these cases we will |
180 | | simply expand the cell size to the width of the unrotated text. */ |
181 | 0 | if (mpDoc) |
182 | 0 | { |
183 | 0 | const ScRotateValueItem& rItem = mpDoc->GetAttr( maCellAddress, ATTR_ROTATE_VALUE ); |
184 | 0 | if (rItem.GetValue() != 0_deg100) |
185 | 0 | { |
186 | 0 | tools::Rectangle aParaRect = GetParagraphBoundingBox(); |
187 | 0 | if( !aParaRect.IsEmpty() && (aCellRect.GetWidth() < aParaRect.GetWidth()) ) |
188 | 0 | aCellRect.SetSize( Size( aParaRect.GetWidth(), aCellRect.GetHeight() ) ); |
189 | 0 | } |
190 | 0 | } |
191 | 0 | } |
192 | 0 | if (aCellRect.IsEmpty()) |
193 | 0 | aCellRect.SetPos(Point(-1, -1)); |
194 | 0 | return aCellRect; |
195 | 0 | } |
196 | | |
197 | | //===== XAccessibleContext ============================================== |
198 | | |
199 | | sal_Int64 SAL_CALL |
200 | | ScAccessibleCell::getAccessibleChildCount() |
201 | 0 | { |
202 | 0 | return AccessibleStaticTextBase::getAccessibleChildCount(); |
203 | 0 | } |
204 | | |
205 | | uno::Reference< XAccessible > SAL_CALL |
206 | | ScAccessibleCell::getAccessibleChild(sal_Int64 nIndex) |
207 | 0 | { |
208 | 0 | return AccessibleStaticTextBase::getAccessibleChild(nIndex); |
209 | 0 | } |
210 | | |
211 | | sal_Int64 SAL_CALL |
212 | | ScAccessibleCell::getAccessibleStateSet() |
213 | 0 | { |
214 | 0 | SolarMutexGuard aGuard; |
215 | 0 | sal_Int64 nParentStates = 0; |
216 | 0 | if (getAccessibleParent().is()) |
217 | 0 | { |
218 | 0 | uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext(); |
219 | 0 | nParentStates = xParentContext->getAccessibleStateSet(); |
220 | 0 | } |
221 | 0 | sal_Int64 nStateSet = 0; |
222 | 0 | if (IsDefunc(nParentStates)) |
223 | 0 | nStateSet |= AccessibleStateType::DEFUNC; |
224 | 0 | else |
225 | 0 | { |
226 | 0 | if (IsFocused()) |
227 | 0 | nStateSet |= AccessibleStateType::FOCUSED; |
228 | |
|
229 | 0 | if (IsFormulaMode()) |
230 | 0 | { |
231 | 0 | nStateSet |= AccessibleStateType::ENABLED; |
232 | 0 | nStateSet |= AccessibleStateType::MULTI_LINE; |
233 | 0 | nStateSet |= AccessibleStateType::MULTI_SELECTABLE; |
234 | 0 | if (IsOpaque()) |
235 | 0 | nStateSet |= AccessibleStateType::OPAQUE; |
236 | 0 | nStateSet |= AccessibleStateType::SELECTABLE; |
237 | 0 | if (IsSelected()) |
238 | 0 | nStateSet |= AccessibleStateType::SELECTED; |
239 | 0 | if (isShowing()) |
240 | 0 | nStateSet |= AccessibleStateType::SHOWING; |
241 | 0 | nStateSet |= AccessibleStateType::TRANSIENT; |
242 | 0 | if (isVisible()) |
243 | 0 | nStateSet |= AccessibleStateType::VISIBLE; |
244 | 0 | return nStateSet; |
245 | 0 | } |
246 | 0 | if (IsEditable(nParentStates)) |
247 | 0 | { |
248 | 0 | nStateSet |= AccessibleStateType::EDITABLE; |
249 | 0 | nStateSet |= AccessibleStateType::RESIZABLE; |
250 | 0 | } |
251 | 0 | nStateSet |= AccessibleStateType::ENABLED; |
252 | 0 | nStateSet |= AccessibleStateType::MULTI_LINE; |
253 | 0 | nStateSet |= AccessibleStateType::MULTI_SELECTABLE; |
254 | 0 | nStateSet |= AccessibleStateType::FOCUSABLE; |
255 | 0 | if (IsOpaque()) |
256 | 0 | nStateSet |= AccessibleStateType::OPAQUE; |
257 | 0 | nStateSet |= AccessibleStateType::SELECTABLE; |
258 | 0 | if (IsSelected()) |
259 | 0 | nStateSet |= AccessibleStateType::SELECTED; |
260 | 0 | if (isShowing()) |
261 | 0 | nStateSet |= AccessibleStateType::SHOWING; |
262 | 0 | nStateSet |= AccessibleStateType::TRANSIENT; |
263 | 0 | if (isVisible()) |
264 | 0 | nStateSet |= AccessibleStateType::VISIBLE; |
265 | 0 | } |
266 | 0 | return nStateSet; |
267 | 0 | } |
268 | | |
269 | | uno::Reference<XAccessibleRelationSet> SAL_CALL |
270 | | ScAccessibleCell::getAccessibleRelationSet() |
271 | 0 | { |
272 | 0 | SolarMutexGuard aGuard; |
273 | 0 | ensureAlive(); |
274 | 0 | rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSet; |
275 | 0 | if (mpAccDoc) |
276 | 0 | pRelationSet = mpAccDoc->GetRelationSet(&maCellAddress); |
277 | 0 | if (!pRelationSet) |
278 | 0 | pRelationSet = new utl::AccessibleRelationSetHelper(); |
279 | 0 | FillDependents(pRelationSet.get()); |
280 | 0 | FillPrecedents(pRelationSet.get()); |
281 | 0 | return pRelationSet; |
282 | 0 | } |
283 | | |
284 | | bool ScAccessibleCell::IsDefunc(sal_Int64 nParentStates) |
285 | 0 | { |
286 | 0 | return ScAccessibleContextBase::IsDefunc() || (mpDoc == nullptr) || (mpViewShell == nullptr) || !getAccessibleParent().is() || |
287 | 0 | (nParentStates & AccessibleStateType::DEFUNC); |
288 | 0 | } |
289 | | |
290 | | bool ScAccessibleCell::IsEditable(sal_Int64 nParentStates) |
291 | 0 | { |
292 | 0 | bool bEditable(true); |
293 | 0 | if ( !(nParentStates & AccessibleStateType::EDITABLE) && |
294 | 0 | mpDoc) |
295 | 0 | { |
296 | | // here I have to test whether the protection of the table should influence this cell. |
297 | 0 | const ScProtectionAttr& rItem = mpDoc->GetAttr(maCellAddress, ATTR_PROTECTION); |
298 | 0 | bEditable = !rItem.GetProtection(); |
299 | 0 | } |
300 | 0 | return bEditable; |
301 | 0 | } |
302 | | |
303 | | bool ScAccessibleCell::IsOpaque() const |
304 | 0 | { |
305 | | // test whether there is a background color |
306 | 0 | bool bOpaque(true); |
307 | 0 | if (mpDoc) |
308 | 0 | { |
309 | 0 | const SvxBrushItem& rItem = mpDoc->GetAttr(maCellAddress, ATTR_BACKGROUND); |
310 | 0 | bOpaque = rItem.GetColor() != COL_TRANSPARENT; |
311 | 0 | } |
312 | 0 | return bOpaque; |
313 | 0 | } |
314 | | |
315 | | bool ScAccessibleCell::IsFocused() const |
316 | 0 | { |
317 | 0 | if (mpViewShell && mpViewShell->GetViewData().GetCurPos() == maCellAddress) |
318 | 0 | return mpViewShell->GetActiveWin()->HasFocus(); |
319 | | |
320 | 0 | return false; |
321 | 0 | } |
322 | | |
323 | | bool ScAccessibleCell::IsSelected() |
324 | 0 | { |
325 | 0 | if (IsFormulaMode()) |
326 | 0 | { |
327 | 0 | const ScAccessibleSpreadsheet* pSheet |
328 | 0 | = static_cast<const ScAccessibleSpreadsheet*>(mpParent.get()); |
329 | 0 | if (pSheet) |
330 | 0 | { |
331 | 0 | return pSheet->IsScAddrFormulaSel(maCellAddress); |
332 | 0 | } |
333 | 0 | return false; |
334 | 0 | } |
335 | | |
336 | 0 | bool bResult(false); |
337 | 0 | if (mpViewShell) |
338 | 0 | { |
339 | 0 | const ScMarkData& rMarkdata = mpViewShell->GetViewData().GetMarkData(); |
340 | 0 | bResult = rMarkdata.IsCellMarked(maCellAddress.Col(), maCellAddress.Row()); |
341 | 0 | } |
342 | 0 | return bResult; |
343 | 0 | } |
344 | | |
345 | | ScDocument* ScAccessibleCell::GetDocument(ScTabViewShell* pViewShell) |
346 | 0 | { |
347 | 0 | ScDocument* pDoc = nullptr; |
348 | 0 | if (pViewShell) |
349 | 0 | pDoc = &pViewShell->GetViewData().GetDocument(); |
350 | 0 | return pDoc; |
351 | 0 | } |
352 | | |
353 | | ::std::unique_ptr< SvxEditSource > ScAccessibleCell::CreateEditSource(ScTabViewShell* pViewShell, ScAddress aCell, ScSplitPos eSplitPos) |
354 | 0 | { |
355 | 0 | if (IsFormulaMode()) |
356 | 0 | { |
357 | 0 | return ::std::unique_ptr< SvxEditSource >(); |
358 | 0 | } |
359 | 0 | ::std::unique_ptr< SvxEditSource > pEditSource (new ScAccessibilityEditSource(std::make_unique<ScAccessibleCellTextData>(pViewShell, aCell, eSplitPos, this))); |
360 | |
|
361 | 0 | return pEditSource; |
362 | 0 | } |
363 | | |
364 | | void ScAccessibleCell::FillDependents(utl::AccessibleRelationSetHelper* pRelationSet) |
365 | 0 | { |
366 | 0 | if (!mpDoc) |
367 | 0 | return; |
368 | | |
369 | 0 | ScRange aRange(0, 0, maCellAddress.Tab(), mpDoc->MaxCol(), mpDoc->MaxRow(), maCellAddress.Tab()); |
370 | 0 | ScCellIterator aCellIter(*mpDoc, aRange); |
371 | |
|
372 | 0 | for (bool bHasCell = aCellIter.first(); bHasCell; bHasCell = aCellIter.next()) |
373 | 0 | { |
374 | 0 | if (aCellIter.getType() == CELLTYPE_FORMULA) |
375 | 0 | { |
376 | 0 | bool bFound = false; |
377 | 0 | ScDetectiveRefIter aIter(*mpDoc, aCellIter.getFormulaCell()); |
378 | 0 | ScRange aRef; |
379 | 0 | while ( !bFound && aIter.GetNextRef( aRef ) ) |
380 | 0 | { |
381 | 0 | if (aRef.Contains(maCellAddress)) |
382 | 0 | bFound = true; |
383 | 0 | } |
384 | 0 | if (bFound) |
385 | 0 | AddRelation(aCellIter.GetPos(), AccessibleRelationType_CONTROLLER_FOR, pRelationSet); |
386 | 0 | } |
387 | 0 | } |
388 | 0 | } |
389 | | |
390 | | void ScAccessibleCell::FillPrecedents(utl::AccessibleRelationSetHelper* pRelationSet) |
391 | 0 | { |
392 | 0 | if (!mpDoc) |
393 | 0 | return; |
394 | | |
395 | 0 | ScRefCellValue aCell(*mpDoc, maCellAddress); |
396 | 0 | if (aCell.getType() == CELLTYPE_FORMULA) |
397 | 0 | { |
398 | 0 | ScFormulaCell* pCell = aCell.getFormula(); |
399 | 0 | ScDetectiveRefIter aIter(*mpDoc, pCell); |
400 | 0 | ScRange aRef; |
401 | 0 | while ( aIter.GetNextRef( aRef ) ) |
402 | 0 | { |
403 | 0 | AddRelation( aRef, AccessibleRelationType_CONTROLLED_BY, pRelationSet); |
404 | 0 | } |
405 | 0 | } |
406 | 0 | } |
407 | | |
408 | | void ScAccessibleCell::AddRelation(const ScAddress& rCell, |
409 | | const AccessibleRelationType eRelationType, |
410 | | utl::AccessibleRelationSetHelper* pRelationSet) |
411 | 0 | { |
412 | 0 | AddRelation(ScRange(rCell, rCell), eRelationType, pRelationSet); |
413 | 0 | } |
414 | | |
415 | | void ScAccessibleCell::AddRelation(const ScRange& rRange, |
416 | | const AccessibleRelationType eRelationType, |
417 | | utl::AccessibleRelationSetHelper* pRelationSet) |
418 | 0 | { |
419 | 0 | uno::Reference < XAccessibleTable > xTable ( getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY ); |
420 | 0 | if (!xTable.is()) |
421 | 0 | return; |
422 | | |
423 | 0 | const sal_uInt32 nCount(static_cast<sal_uInt32>(rRange.aEnd.Col() - |
424 | 0 | rRange.aStart.Col() + 1) * (rRange.aEnd.Row() - |
425 | 0 | rRange.aStart.Row() + 1)); |
426 | | |
427 | | // tdf#157299 avoid handling a large amount of cells for performance reasons |
428 | 0 | if (nCount > 1000) |
429 | 0 | { |
430 | 0 | SAL_WARN("sc", "ScAccessibleCell::AddRelation: Not setting relations " |
431 | 0 | "for cell range with more than 1000 cells for performance reasons."); |
432 | 0 | return; |
433 | 0 | } |
434 | | |
435 | 0 | uno::Sequence<uno::Reference<css::accessibility::XAccessible>> aTargetSet(nCount); |
436 | 0 | uno::Reference <css::accessibility::XAccessible>* pTargetSet = aTargetSet.getArray(); |
437 | 0 | sal_uInt32 nPos(0); |
438 | 0 | for (sal_uInt32 nRow = rRange.aStart.Row(); nRow <= sal::static_int_cast<sal_uInt32>(rRange.aEnd.Row()); ++nRow) |
439 | 0 | { |
440 | 0 | for (sal_uInt32 nCol = rRange.aStart.Col(); nCol <= sal::static_int_cast<sal_uInt32>(rRange.aEnd.Col()); ++nCol) |
441 | 0 | { |
442 | 0 | pTargetSet[nPos] = xTable->getAccessibleCellAt(nRow, nCol); |
443 | 0 | ++nPos; |
444 | 0 | } |
445 | 0 | } |
446 | 0 | OSL_ENSURE(nCount == nPos, "something went wrong"); |
447 | 0 | AccessibleRelation aRelation; |
448 | 0 | aRelation.RelationType = eRelationType; |
449 | 0 | aRelation.TargetSet = std::move(aTargetSet); |
450 | 0 | pRelationSet->AddRelation(aRelation); |
451 | 0 | } |
452 | | |
453 | | static OUString ReplaceFourChar(const OUString& oldOUString) |
454 | 0 | { |
455 | 0 | return oldOUString.replaceAll(u"\\", u"\\\\") |
456 | 0 | .replaceAll(u";", u"\\;") |
457 | 0 | .replaceAll(u"=", u"\\=") |
458 | 0 | .replaceAll(u",", u"\\,") |
459 | 0 | .replaceAll(u":", u"\\:"); |
460 | 0 | } |
461 | | |
462 | | std::unordered_map<OUString, OUString> ScAccessibleCell::implGetExtendedAttributes() |
463 | 0 | { |
464 | 0 | const OUString sRowIndexText = maCellAddress.Format(ScRefFlags::ROW_VALID); |
465 | 0 | const OUString sColIndexText = maCellAddress.Format(ScRefFlags::COL_VALID); |
466 | 0 | std::unordered_map<OUString, OUString> aAttributes |
467 | 0 | = { { AccessibleAttribute::RowIndexText, sRowIndexText }, |
468 | 0 | { AccessibleAttribute::ColIndexText, sColIndexText } }; |
469 | |
|
470 | 0 | if (mpViewShell) |
471 | 0 | { |
472 | 0 | const OUString sFormula = mpViewShell->GetFormula(maCellAddress) ; |
473 | 0 | if (!sFormula.isEmpty()) |
474 | 0 | aAttributes.emplace(u"Formula"_ustr, ReplaceFourChar(sFormula.copy(1))); |
475 | |
|
476 | 0 | aAttributes.emplace(u"Note"_ustr, ReplaceFourChar(GetAllDisplayNote())); |
477 | 0 | for (const auto& rAttr : getShadowAttrs()) |
478 | 0 | aAttributes.insert(rAttr); |
479 | 0 | for (const auto& rAttr : getBorderAttrs()) |
480 | 0 | aAttributes.insert(rAttr); |
481 | | //end of cell attributes |
482 | 0 | if( mpDoc ) |
483 | 0 | { |
484 | 0 | const OUString sDropDownValue = IsDropdown() ? u"true"_ustr : u"false"_ustr; |
485 | 0 | aAttributes.emplace(u"isdropdown"_ustr, sDropDownValue); |
486 | 0 | } |
487 | 0 | } |
488 | |
|
489 | 0 | return aAttributes; |
490 | 0 | } |
491 | | |
492 | | // cell has its own ParaIndent property, so when calling character attributes on cell, the ParaIndent should replace the ParaLeftMargin if its value is not zero. |
493 | | uno::Sequence< beans::PropertyValue > SAL_CALL ScAccessibleCell::getCharacterAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& aRequestedAttributes ) |
494 | 0 | { |
495 | 0 | SolarMutexGuard aGuard; |
496 | |
|
497 | 0 | uno::Sequence< beans::PropertyValue > aAttribs = AccessibleStaticTextBase::getCharacterAttributes( nIndex, aRequestedAttributes ); |
498 | |
|
499 | 0 | sal_uInt16 nParaIndent = mpDoc->GetAttr( maCellAddress, ATTR_INDENT ).GetValue(); |
500 | 0 | if (nParaIndent > 0) |
501 | 0 | { |
502 | 0 | auto [begin, end] = asNonConstRange(aAttribs); |
503 | 0 | auto pAttrib = std::find_if(begin, end, |
504 | 0 | [](const beans::PropertyValue& rAttrib) { return "ParaLeftMargin" == rAttrib.Name; }); |
505 | 0 | if (pAttrib != end) |
506 | 0 | pAttrib->Value <<= nParaIndent; |
507 | 0 | } |
508 | 0 | return aAttribs; |
509 | 0 | } |
510 | | |
511 | | bool ScAccessibleCell::IsFormulaMode() |
512 | 0 | { |
513 | 0 | ScAccessibleSpreadsheet* pSheet = static_cast<ScAccessibleSpreadsheet*>(mpParent.get()); |
514 | 0 | if (pSheet) |
515 | 0 | { |
516 | 0 | return pSheet->IsFormulaMode(); |
517 | 0 | } |
518 | 0 | return false; |
519 | 0 | } |
520 | | |
521 | | bool ScAccessibleCell::IsDropdown() const |
522 | 0 | { |
523 | 0 | sal_uInt16 nPosX = maCellAddress.Col(); |
524 | 0 | sal_uInt16 nPosY = sal_uInt16(maCellAddress.Row()); |
525 | 0 | sal_uInt16 nTab = maCellAddress.Tab(); |
526 | 0 | sal_uInt32 nValidation = mpDoc->GetAttr( nPosX, nPosY, nTab, ATTR_VALIDDATA ).GetValue(); |
527 | 0 | if( nValidation ) |
528 | 0 | { |
529 | 0 | const ScValidationData* pData = mpDoc->GetValidationEntry( nValidation ); |
530 | 0 | if( pData && pData->HasSelectionList() ) |
531 | 0 | return true; |
532 | 0 | } |
533 | 0 | const ScMergeFlagAttr& rAttr = mpDoc->GetAttr( nPosX, nPosY, nTab, ATTR_MERGE_FLAG ); |
534 | 0 | if (rAttr.HasAutoFilter()) |
535 | 0 | { |
536 | 0 | return true; |
537 | 0 | } |
538 | 0 | else |
539 | 0 | { |
540 | 0 | sal_uInt16 nTabCount = mpDoc->GetTableCount(); |
541 | 0 | if ( nTab+1<nTabCount && mpDoc->IsScenario(nTab+1) && !mpDoc->IsScenario(nTab) ) |
542 | 0 | { |
543 | 0 | SCTAB i; |
544 | 0 | ScMarkData aMarks(mpDoc->GetSheetLimits()); |
545 | 0 | for (i=nTab+1; i<nTabCount && mpDoc->IsScenario(i); i++) |
546 | 0 | mpDoc->MarkScenario( i, nTab, aMarks, false, ScScenarioFlags::ShowFrame ); |
547 | 0 | ScRangeList aRanges; |
548 | 0 | aMarks.FillRangeListWithMarks( &aRanges, false ); |
549 | 0 | bool bHasScenario; |
550 | 0 | SCTAB nRangeCount = aRanges.size(); |
551 | 0 | for (i=0; i<nRangeCount; i++) |
552 | 0 | { |
553 | 0 | ScRange aRange = aRanges[i]; |
554 | 0 | mpDoc->ExtendTotalMerge( aRange ); |
555 | 0 | bool bTextBelow = ( aRange.aStart.Row() == 0 ); |
556 | | // MT IA2: Not used: sal_Bool bIsInScen = sal_False; |
557 | 0 | if ( bTextBelow ) |
558 | 0 | { |
559 | 0 | bHasScenario = (aRange.aStart.Col() == nPosX && aRange.aEnd.Row() == nPosY-1); |
560 | 0 | } |
561 | 0 | else |
562 | 0 | { |
563 | 0 | bHasScenario = (aRange.aStart.Col() == nPosX && aRange.aStart.Row() == nPosY+1); |
564 | 0 | } |
565 | 0 | if( bHasScenario ) return true; |
566 | 0 | } |
567 | 0 | } |
568 | 0 | } |
569 | 0 | return false; |
570 | 0 | } |
571 | | |
572 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |