Get the style color and number just once
[LibreOffice.git] / svx / source / table / accessiblecell.cxx
blob65b74d4ae6dec69b3c63c4270946ea1f48600619
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
23 #include <memory>
25 #include "accessiblecell.hxx"
26 #include <cell.hxx>
28 #include <com/sun/star/accessibility/AccessibleRole.hpp>
29 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
30 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
32 #include <editeng/unoedsrc.hxx>
33 #include <utility>
34 #include <vcl/svapp.hxx>
35 #include <vcl/window.hxx>
37 #include <comphelper/string.hxx>
38 #include <comphelper/sequence.hxx>
39 #include <svx/IAccessibleViewForwarder.hxx>
40 #include <svx/unoshtxt.hxx>
41 #include <svx/svdotext.hxx>
42 #include <tools/debug.hxx>
44 using namespace sdr::table;
45 using namespace ::com::sun::star;
46 using namespace ::com::sun::star::uno;
47 using namespace ::com::sun::star::accessibility;
48 using namespace ::com::sun::star::lang;
50 namespace accessibility {
52 AccessibleCell::AccessibleCell( const rtl::Reference< AccessibleTableShape>& rxParent, sdr::table::CellRef xCell, sal_Int32 nIndex, const AccessibleShapeTreeInfo& rShapeTreeInfo )
53 : AccessibleCellBase( rxParent, AccessibleRole::TABLE_CELL )
54 , maShapeTreeInfo( rShapeTreeInfo )
55 , mnIndexInParent( nIndex )
56 , mxCell(std::move( xCell ))
58 //Init the pAccTable var
59 pAccTable = rxParent.get();
63 AccessibleCell::~AccessibleCell()
65 DBG_ASSERT( mpText == nullptr, "svx::AccessibleCell::~AccessibleCell(), not disposed!?" );
69 void AccessibleCell::Init()
71 SdrView* pView = maShapeTreeInfo.GetSdrView();
72 const vcl::Window* pWindow = maShapeTreeInfo.GetWindow ();
73 if( !((pView != nullptr) && (pWindow != nullptr) && mxCell.is()))
74 return;
76 // create AccessibleTextHelper to handle this shape's text
77 if( mxCell->CanCreateEditOutlinerParaObject() || mxCell->GetOutlinerParaObject() != nullptr )
79 // non-empty text -> use full-fledged edit source right away
81 mpText.reset( new AccessibleTextHelper( std::make_unique<SvxTextEditSource>(mxCell->GetObject(), mxCell.get(), *pView, *pWindow->GetOutDev()) ) );
82 if( mxCell.is() && mxCell->IsActiveCell() )
83 mpText->SetFocus();
84 mpText->SetEventSource(this);
89 bool AccessibleCell::SetState (sal_Int64 aState)
91 bool bStateHasChanged = false;
93 if (aState == AccessibleStateType::FOCUSED && mpText != nullptr)
95 // Offer FOCUSED state to edit engine and detect whether the state
96 // changes.
97 bool bIsFocused = mpText->HaveFocus ();
98 mpText->SetFocus();
99 bStateHasChanged = (bIsFocused != mpText->HaveFocus ());
101 else
102 bStateHasChanged = AccessibleContextBase::SetState (aState);
104 return bStateHasChanged;
108 bool AccessibleCell::ResetState (sal_Int64 aState)
110 bool bStateHasChanged = false;
112 if (aState == AccessibleStateType::FOCUSED && mpText != nullptr)
114 // Try to remove FOCUSED state from the edit engine and detect
115 // whether the state changes.
116 bool bIsFocused = mpText->HaveFocus ();
117 mpText->SetFocus (false);
118 bStateHasChanged = (bIsFocused != mpText->HaveFocus ());
120 else
121 bStateHasChanged = AccessibleContextBase::ResetState (aState);
123 return bStateHasChanged;
127 // XInterface
130 Any SAL_CALL AccessibleCell::queryInterface( const Type& aType )
132 return AccessibleCellBase::queryInterface( aType );
136 void SAL_CALL AccessibleCell::acquire( ) noexcept
138 AccessibleCellBase::acquire();
142 void SAL_CALL AccessibleCell::release( ) noexcept
144 AccessibleCellBase::release();
148 // XAccessibleContext
151 /** The children of this cell come from the paragraphs of text.
153 sal_Int64 SAL_CALL AccessibleCell::getAccessibleChildCount()
155 SolarMutexGuard aSolarGuard;
156 ThrowIfDisposed ();
157 return mpText != nullptr ? mpText->GetChildCount () : 0;
161 /** Forward the request to the shape. Return the requested shape or throw
162 an exception for a wrong index.
164 Reference<XAccessible> SAL_CALL AccessibleCell::getAccessibleChild (sal_Int64 nIndex)
166 SolarMutexGuard aSolarGuard;
167 ThrowIfDisposed ();
169 return mpText->GetChild (nIndex);
173 /** Return a copy of the state set.
174 Possible states are:
175 ENABLED
176 SHOWING
177 VISIBLE
179 sal_Int64 SAL_CALL AccessibleCell::getAccessibleStateSet()
181 SolarMutexGuard aSolarGuard;
182 ::osl::MutexGuard aGuard (m_aMutex);
183 sal_Int64 nStateSet = 0;
185 if (rBHelper.bDisposed || mpText == nullptr)
187 // Return a minimal state set that only contains the DEFUNC state.
188 nStateSet = AccessibleContextBase::getAccessibleStateSet ();
190 else
192 // Merge current FOCUSED state from edit engine.
193 if (mpText != nullptr)
195 if (mpText->HaveFocus())
196 mnStateSet |= AccessibleStateType::FOCUSED;
197 else
198 mnStateSet &= ~AccessibleStateType::FOCUSED;
200 // Set the invisible state for merged cell
201 if (mxCell.is() && mxCell->isMerged())
202 mnStateSet &= ~AccessibleStateType::VISIBLE;
203 else
204 mnStateSet |= AccessibleStateType::VISIBLE;
207 //Just when the parent table is not read-only,set states EDITABLE,RESIZABLE,MOVEABLE
208 css::uno::Reference<XAccessible> xTempAcc = getAccessibleParent();
209 if( xTempAcc.is() )
211 css::uno::Reference<XAccessibleContext>
212 xTempAccContext = xTempAcc->getAccessibleContext();
213 if( xTempAccContext.is() )
215 if (xTempAccContext->getAccessibleStateSet() & AccessibleStateType::EDITABLE)
217 mnStateSet |= AccessibleStateType::EDITABLE;
218 mnStateSet |= AccessibleStateType::RESIZABLE;
219 mnStateSet |= AccessibleStateType::MOVEABLE;
223 nStateSet = mnStateSet;
226 return nStateSet;
230 // XAccessibleComponent
233 sal_Bool SAL_CALL AccessibleCell::containsPoint( const css::awt::Point& aPoint)
235 return AccessibleComponentBase::containsPoint( aPoint );
238 /** The implementation below is at the moment straightforward. It iterates
239 over all children (and thereby instances all children which have not
240 been already instantiated) until a child covering the specified point is
241 found.
242 This leaves room for improvement. For instance, first iterate only over
243 the already instantiated children and only if no match is found
244 instantiate the remaining ones.
246 Reference<XAccessible > SAL_CALL AccessibleCell::getAccessibleAtPoint ( const css::awt::Point& aPoint)
248 SolarMutexGuard aSolarGuard;
249 ::osl::MutexGuard aGuard (m_aMutex);
251 sal_Int64 nChildCount = getAccessibleChildCount ();
252 for (sal_Int64 i = 0; i < nChildCount; ++i)
254 Reference<XAccessible> xChild (getAccessibleChild (i));
255 if (xChild.is())
257 Reference<XAccessibleComponent> xChildComponent (xChild->getAccessibleContext(), uno::UNO_QUERY);
258 if (xChildComponent.is())
260 awt::Rectangle aBBox (xChildComponent->getBounds());
261 if ( (aPoint.X >= aBBox.X)
262 && (aPoint.Y >= aBBox.Y)
263 && (aPoint.X < aBBox.X+aBBox.Width)
264 && (aPoint.Y < aBBox.Y+aBBox.Height) )
265 return xChild;
270 // Have not found a child under the given point. Returning empty
271 // reference to indicate this.
272 return uno::Reference<XAccessible>();
276 css::awt::Rectangle SAL_CALL AccessibleCell::getBounds()
278 SolarMutexGuard aSolarGuard;
279 ::osl::MutexGuard aGuard (m_aMutex);
281 ThrowIfDisposed ();
282 css::awt::Rectangle aBoundingBox;
283 if( mxCell.is() )
285 // Get the cell's bounding box in internal coordinates (in 100th of mm)
286 const ::tools::Rectangle aCellRect( mxCell->getCellRect() );
288 // Transform coordinates from internal to pixel.
289 if (maShapeTreeInfo.GetViewForwarder() == nullptr)
290 throw uno::RuntimeException (u"AccessibleCell has no valid view forwarder"_ustr, getXWeak());
292 ::Size aPixelSize( maShapeTreeInfo.GetViewForwarder()->LogicToPixel(::Size(aCellRect.GetWidth(), aCellRect.GetHeight())) );
293 ::Point aPixelPosition( maShapeTreeInfo.GetViewForwarder()->LogicToPixel( aCellRect.TopLeft() ));
295 // Clip the shape's bounding box with the bounding box of its parent.
296 Reference<XAccessibleComponent> xParentComponent ( getAccessibleParent(), uno::UNO_QUERY);
297 if (xParentComponent.is())
299 // Make the coordinates relative to the parent.
300 awt::Point aParentLocation (xParentComponent->getLocationOnScreen());
301 int x = aPixelPosition.getX() - aParentLocation.X;
302 int y = aPixelPosition.getY() - aParentLocation.Y;
304 // Clip with parent (with coordinates relative to itself).
305 ::tools::Rectangle aBBox ( x, y, x + aPixelSize.getWidth(), y + aPixelSize.getHeight());
306 awt::Size aParentSize (xParentComponent->getSize());
307 ::tools::Rectangle aParentBBox (0,0, aParentSize.Width, aParentSize.Height);
308 aBBox = aBBox.GetIntersection (aParentBBox);
309 aBoundingBox = awt::Rectangle ( aBBox.Left(), aBBox.Top(), aBBox.getOpenWidth(), aBBox.getOpenHeight());
311 else
313 SAL_INFO("svx", "parent does not support component");
314 aBoundingBox = awt::Rectangle (aPixelPosition.getX(), aPixelPosition.getY(),aPixelSize.getWidth(), aPixelSize.getHeight());
318 return aBoundingBox;
322 css::awt::Point SAL_CALL AccessibleCell::getLocation()
324 ThrowIfDisposed ();
325 css::awt::Rectangle aBoundingBox(getBounds());
326 return css::awt::Point(aBoundingBox.X, aBoundingBox.Y);
330 css::awt::Point SAL_CALL AccessibleCell::getLocationOnScreen()
332 ThrowIfDisposed ();
334 // Get relative position...
335 css::awt::Point aLocation(getLocation ());
337 // ... and add absolute position of the parent.
338 Reference<XAccessibleComponent> xParentComponent( getAccessibleParent(), uno::UNO_QUERY);
339 if(xParentComponent.is())
341 css::awt::Point aParentLocation(xParentComponent->getLocationOnScreen());
342 aLocation.X += aParentLocation.X;
343 aLocation.Y += aParentLocation.Y;
345 else
347 SAL_WARN("svx", "parent does not support XAccessibleComponent");
350 return aLocation;
354 awt::Size SAL_CALL AccessibleCell::getSize()
356 ThrowIfDisposed ();
357 awt::Rectangle aBoundingBox (getBounds());
358 return awt::Size (aBoundingBox.Width, aBoundingBox.Height);
362 void SAL_CALL AccessibleCell::grabFocus()
364 AccessibleComponentBase::grabFocus();
368 sal_Int32 SAL_CALL AccessibleCell::getForeground()
370 ThrowIfDisposed ();
372 // todo
373 return sal_Int32(0x0ffffffL);
377 sal_Int32 SAL_CALL AccessibleCell::getBackground()
379 ThrowIfDisposed ();
381 // todo
382 return 0;
386 // XAccessibleExtendedComponent
388 OUString SAL_CALL AccessibleCell::getTitledBorderText()
390 return AccessibleComponentBase::getTitledBorderText();
394 OUString SAL_CALL AccessibleCell::getToolTipText()
396 return AccessibleComponentBase::getToolTipText();
400 // XAccessibleEventBroadcaster
403 void SAL_CALL AccessibleCell::addAccessibleEventListener( const Reference<XAccessibleEventListener >& rxListener)
405 SolarMutexGuard aSolarGuard;
406 ::osl::MutexGuard aGuard (m_aMutex);
407 if (rBHelper.bDisposed || rBHelper.bInDispose)
409 Reference<XInterface> xSource( static_cast<XComponent *>(this) );
410 lang::EventObject aEventObj(xSource);
411 rxListener->disposing(aEventObj);
413 else
415 AccessibleContextBase::addAccessibleEventListener (rxListener);
416 if (mpText != nullptr)
417 mpText->AddEventListener (rxListener);
422 void SAL_CALL AccessibleCell::removeAccessibleEventListener( const Reference<XAccessibleEventListener >& rxListener)
424 SolarMutexGuard aSolarGuard;
425 AccessibleContextBase::removeAccessibleEventListener(rxListener);
426 if (mpText != nullptr)
427 mpText->RemoveEventListener (rxListener);
431 // XServiceInfo
434 OUString SAL_CALL AccessibleCell::getImplementationName()
436 return u"AccessibleCell"_ustr;
440 Sequence<OUString> SAL_CALL AccessibleCell::getSupportedServiceNames()
442 ThrowIfDisposed ();
443 const css::uno::Sequence<OUString> vals { u"com.sun.star.drawing.AccessibleCell"_ustr };
444 return comphelper::concatSequences(AccessibleContextBase::getSupportedServiceNames(), vals);
448 // IAccessibleViewForwarderListener
451 void AccessibleCell::ViewForwarderChanged()
453 // Inform all listeners that the graphical representation (i.e. size
454 // and/or position) of the shape has changed.
455 CommitChange(AccessibleEventId::VISIBLE_DATA_CHANGED, Any(), Any(), -1);
457 // update our children that our screen position might have changed
458 if( mpText )
459 mpText->UpdateChildren();
463 // protected
466 void AccessibleCell::disposing()
468 SolarMutexGuard aSolarGuard;
469 ::osl::MutexGuard aGuard (m_aMutex);
471 // Make sure to send an event that this object loses the focus in the
472 // case that it has the focus.
473 mnStateSet &= ~AccessibleStateType::FOCUSED;
475 if (mpText != nullptr)
477 mpText->Dispose();
478 mpText.reset();
481 // Cleanup. Remove references to objects to allow them to be
482 // destroyed.
483 mxCell.clear();
484 maShapeTreeInfo.dispose();
486 // Call base classes.
487 AccessibleContextBase::dispose ();
490 sal_Int64 SAL_CALL AccessibleCell::getAccessibleIndexInParent()
492 ThrowIfDisposed ();
493 return mnIndexInParent;
497 OUString AccessibleCell::getCellName( sal_Int32 nCol, sal_Int32 nRow )
499 OUStringBuffer aBuf;
501 if (nCol < 26*26)
503 if (nCol < 26)
504 aBuf.append( static_cast<sal_Unicode>( 'A' +
505 static_cast<sal_uInt16>(nCol)));
506 else
508 aBuf.append(
509 OUStringChar(static_cast<sal_Unicode>( 'A' +
510 (static_cast<sal_uInt16>(nCol) / 26) - 1))
511 + OUStringChar( static_cast<sal_Unicode>( 'A' +
512 (static_cast<sal_uInt16>(nCol) % 26))) );
515 else
517 OUStringBuffer aStr;
518 while (nCol >= 26)
520 sal_Int32 nC = nCol % 26;
521 aStr.append(static_cast<sal_Unicode>( 'A' +
522 static_cast<sal_uInt16>(nC)));
523 nCol = nCol - nC;
524 nCol = nCol / 26 - 1;
526 aStr.append(static_cast<sal_Unicode>( 'A' +
527 static_cast<sal_uInt16>(nCol)));
528 aBuf.append(comphelper::string::reverseString(aStr));
530 aBuf.append(nRow+1);
531 return aBuf.makeStringAndClear();
534 OUString SAL_CALL AccessibleCell::getAccessibleName()
536 ThrowIfDisposed ();
537 SolarMutexGuard aSolarGuard;
539 if( pAccTable )
543 sal_Int32 nRow = 0, nCol = 0;
544 pAccTable->getColumnAndRow(mnIndexInParent, nCol, nRow);
545 return getCellName( nCol, nRow );
547 catch(const Exception&)
552 return AccessibleCellBase::getAccessibleName();
555 void AccessibleCell::UpdateChildren()
557 if (mpText)
558 mpText->UpdateChildren();
561 /* MT: Above getAccessibleName was introduced with IA2 CWS, while below was introduce in 3.3 meanwhile. Check which one is correct
562 +If this is correct, we also don't need sdr::table::CellRef getCellRef(), UpdateChildren(), getCellName( sal_Int32 nCol, sal_Int32 nRow ) above
565 OUString SAL_CALL AccessibleCell::getAccessibleName() throw (css::uno::RuntimeException)
567 ThrowIfDisposed ();
568 SolarMutexGuard aSolarGuard;
570 if( mxCell.is() )
571 return mxCell->getName();
573 return AccessibleCellBase::getAccessibleName();
577 } // end of namespace accessibility
579 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */