1 /*************************************************************************
2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3  *
4  * Copyright 2000, 2010 Oracle and/or its affiliates.
5  *
6  * OpenOffice.org - a multi-platform office productivity suite
7  *
8  * This file is part of OpenOffice.org.
9  *
10  * OpenOffice.org is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU Lesser General Public License version 3
12  * only, as published by the Free Software Foundation.
13  *
14  * OpenOffice.org is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Lesser General Public License version 3 for more details
18  * (a copy is included in the LICENSE file that accompanied this code).
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * version 3 along with OpenOffice.org.  If not, see
22  * <http://www.openoffice.org/license.html>
23  * for a copy of the LGPLv3 License.
24  *
25  ************************************************************************/
26 
27 #include "precompiled_accessibility.hxx"
28 
29 #include "accessibility/extended/AccessibleToolPanelDeck.hxx"
30 
31 /** === begin UNO includes === **/
32 #include <com/sun/star/accessibility/AccessibleRole.hpp>
33 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
34 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
35 #include <com/sun/star/lang/DisposedException.hpp>
36 /** === end UNO includes === **/
37 
38 #include <svtools/toolpanel/toolpaneldeck.hxx>
39 #include <toolkit/awt/vclxwindow.hxx>
40 #include <toolkit/helper/vclunohelper.hxx>
41 #include <vcl/svapp.hxx>
42 #include <vos/mutex.hxx>
43 #include <unotools/accessiblestatesethelper.hxx>
44 #include <tools/diagnose_ex.h>
45 
46 #include <boost/noncopyable.hpp>
47 #include <vector>
48 
49 //......................................................................................................................
50 namespace accessibility
51 {
52 //......................................................................................................................
53 
54 	/** === begin UNO using === **/
55 	using ::com::sun::star::uno::Reference;
56 	using ::com::sun::star::uno::XInterface;
57 	using ::com::sun::star::uno::UNO_QUERY;
58 	using ::com::sun::star::uno::UNO_QUERY_THROW;
59 	using ::com::sun::star::uno::UNO_SET_THROW;
60 	using ::com::sun::star::uno::Exception;
61 	using ::com::sun::star::uno::RuntimeException;
62 	using ::com::sun::star::uno::Any;
63 	using ::com::sun::star::uno::makeAny;
64 	using ::com::sun::star::uno::Sequence;
65 	using ::com::sun::star::uno::Type;
66     using ::com::sun::star::accessibility::XAccessible;
67     using ::com::sun::star::accessibility::XAccessibleContext;
68     using ::com::sun::star::lang::DisposedException;
69     using ::com::sun::star::lang::IndexOutOfBoundsException;
70     using ::com::sun::star::lang::Locale;
71     using ::com::sun::star::accessibility::XAccessibleRelationSet;
72     using ::com::sun::star::accessibility::XAccessibleStateSet;
73     using ::com::sun::star::accessibility::IllegalAccessibleComponentStateException;
74     using ::com::sun::star::awt::XFont;
75 	/** === end UNO using === **/
76     namespace AccessibleRole = ::com::sun::star::accessibility::AccessibleRole;
77     namespace AccessibleEventId = ::com::sun::star::accessibility::AccessibleEventId;
78     namespace AccessibleStateType = ::com::sun::star::accessibility::AccessibleStateType;
79 
80     typedef ::com::sun::star::awt::Rectangle    UnoRectangle;
81     typedef ::com::sun::star::awt::Point        UnoPoint;
82 
83 	//==================================================================================================================
84 	//= AccessibleToolPanelDeck_Impl - declaration
85 	//==================================================================================================================
86     class AccessibleToolPanelDeck_Impl  :public ::boost::noncopyable
87                                         ,public ::svt::IToolPanelDeckListener
88     {
89     public:
90         AccessibleToolPanelDeck_Impl(
91             AccessibleToolPanelDeck& i_rAntiImpl,
92             const Reference< XAccessible >& i_rAccessibleParent,
93             ::svt::ToolPanelDeck& i_rPanelDeck
94         );
95 
96         void    checkDisposed();
97         bool    isDisposed() const { return m_pPanelDeck == NULL; }
98         void    dispose();
99 
100         ~AccessibleToolPanelDeck_Impl();
101 
102         Reference< XAccessible >    getOwnAccessible() const;
103         Reference< XAccessible >    getActivePanelAccessible();
104 
105     protected:
106         // IToolPanelDeckListener
107         virtual void PanelInserted( const ::svt::PToolPanel& i_pPanel, const size_t i_nPosition );
108         virtual void PanelRemoved( const size_t i_nPosition );
109         virtual void ActivePanelChanged( const ::boost::optional< size_t >& i_rOldActive, const ::boost::optional< size_t >& i_rNewActive );
110         virtual void LayouterChanged( const ::svt::PDeckLayouter& i_rNewLayouter );
111         virtual void Dying();
112 
113     public:
114         AccessibleToolPanelDeck&    m_rAntiImpl;
115         Reference< XAccessible >    m_xAccessibleParent;
116         ::svt::ToolPanelDeck*       m_pPanelDeck;
117 
118         typedef ::std::vector< Reference< XAccessible > > AccessibleChildren;
119         Reference< XAccessible >        m_xActivePanelAccessible;
120     };
121 
122 	//==================================================================================================================
123 	//= MethodGuard
124 	//==================================================================================================================
125     namespace
126     {
127         class MethodGuard
128         {
129         public:
130             MethodGuard( AccessibleToolPanelDeck_Impl& i_rImpl )
131                 :m_aGuard( Application::GetSolarMutex() )
132             {
133                 i_rImpl.checkDisposed();
134             }
135             ~MethodGuard()
136             {
137             }
138 
139             void clear()
140             {
141                 m_aGuard.clear();
142             }
143 
144         private:
145             ::vos::OClearableGuard  m_aGuard;
146         };
147     }
148 
149 	//==================================================================================================================
150 	//= AccessibleToolPanelDeck_Impl - implementation
151 	//==================================================================================================================
152 	//------------------------------------------------------------------------------------------------------------------
153     AccessibleToolPanelDeck_Impl::AccessibleToolPanelDeck_Impl( AccessibleToolPanelDeck& i_rAntiImpl, const Reference< XAccessible >& i_rAccessibleParent,
154             ::svt::ToolPanelDeck& i_rPanelDeck )
155         :m_rAntiImpl( i_rAntiImpl )
156         ,m_xAccessibleParent( i_rAccessibleParent )
157         ,m_pPanelDeck( &i_rPanelDeck )
158         ,m_xActivePanelAccessible()
159     {
160         m_pPanelDeck->AddListener( *this );
161     }
162 
163 	//------------------------------------------------------------------------------------------------------------------
164     AccessibleToolPanelDeck_Impl::~AccessibleToolPanelDeck_Impl()
165     {
166         if ( !isDisposed() )
167             dispose();
168     }
169 
170 	//------------------------------------------------------------------------------------------------------------------
171     void AccessibleToolPanelDeck_Impl::dispose()
172     {
173         ENSURE_OR_RETURN_VOID( !isDisposed(), "disposed twice" );
174         m_pPanelDeck->RemoveListener( *this );
175         m_pPanelDeck = NULL;
176         m_xAccessibleParent.clear();
177     }
178 
179 	//------------------------------------------------------------------------------------------------------------------
180     void AccessibleToolPanelDeck_Impl::checkDisposed()
181     {
182         if ( isDisposed() )
183             throw DisposedException( ::rtl::OUString(), *&m_rAntiImpl );
184     }
185 
186 	//------------------------------------------------------------------------------------------------------------------
187     Reference< XAccessible > AccessibleToolPanelDeck_Impl::getOwnAccessible() const
188     {
189         Reference< XAccessible > xOwnAccessible( static_cast< XAccessible* >( m_rAntiImpl.GetVCLXWindow() ) );
190         OSL_ENSURE( xOwnAccessible->getAccessibleContext() == Reference< XAccessibleContext >( &m_rAntiImpl ),
191             "AccessibleToolPanelDeck_Impl::getOwnAccessible: could not retrieve proper XAccessible for /myself!" );
192         return xOwnAccessible;
193     }
194 
195 	//------------------------------------------------------------------------------------------------------------------
196     Reference< XAccessible > AccessibleToolPanelDeck_Impl::getActivePanelAccessible()
197     {
198         ENSURE_OR_RETURN( !isDisposed(), "AccessibleToolPanelDeck_Impl::getActivePanelAccessible: already disposed!", NULL );
199 
200         if ( !m_xActivePanelAccessible.is() )
201         {
202             ::boost::optional< size_t > aActivePanel( m_pPanelDeck->GetActivePanel() );
203             ENSURE_OR_RETURN( !!aActivePanel, "AccessibleToolPanelDeck_Impl::getActivePanelAccessible: this should not be called without an active panel!", NULL );
204             ::svt::PToolPanel pActivePanel( m_pPanelDeck->GetPanel( *aActivePanel ) );
205             ENSURE_OR_RETURN( pActivePanel.get() != NULL, "AccessibleToolPanelDeck_Impl::getActivePanelAccessible: no active panel!", NULL );
206             m_xActivePanelAccessible = pActivePanel->CreatePanelAccessible( getOwnAccessible() );
207             OSL_ENSURE( m_xActivePanelAccessible.is(), "AccessibleToolPanelDeck_Impl::getActivePanelAccessible: illegal accessible returned by the panel!" );
208         }
209 
210         return m_xActivePanelAccessible;
211     }
212 
213 	//------------------------------------------------------------------------------------------------------------------
214     void AccessibleToolPanelDeck_Impl::PanelInserted( const ::svt::PToolPanel& i_pPanel, const size_t i_nPosition )
215     {
216         (void)i_pPanel;
217         (void)i_nPosition;
218     }
219 
220 	//------------------------------------------------------------------------------------------------------------------
221     void AccessibleToolPanelDeck_Impl::PanelRemoved( const size_t i_nPosition )
222     {
223         (void)i_nPosition;
224     }
225 
226 	//------------------------------------------------------------------------------------------------------------------
227     void AccessibleToolPanelDeck_Impl::ActivePanelChanged( const ::boost::optional< size_t >& i_rOldActive, const ::boost::optional< size_t >& i_rNewActive )
228     {
229         if ( !!i_rOldActive )
230         {
231             if ( !m_xActivePanelAccessible.is() )
232             {
233                 // again, this might in theory happen if the XAccessible for the active panel has never before been requested.
234                 // In this case, just say that all our children are invalid, so they all must be re-requested.
235                 m_rAntiImpl.NotifyAccessibleEvent( AccessibleEventId::INVALIDATE_ALL_CHILDREN, Any(), Any() );
236             }
237             else
238             {
239                 m_rAntiImpl.NotifyAccessibleEvent( AccessibleEventId::CHILD, makeAny( m_xActivePanelAccessible ), Any() );
240             }
241         }
242 
243         m_xActivePanelAccessible.clear();
244 
245         if ( !!i_rNewActive )
246         {
247             m_rAntiImpl.NotifyAccessibleEvent( AccessibleEventId::CHILD, Any(), makeAny( getActivePanelAccessible() ) );
248         }
249     }
250 
251 	//------------------------------------------------------------------------------------------------------------------
252     void AccessibleToolPanelDeck_Impl::LayouterChanged( const ::svt::PDeckLayouter& i_rNewLayouter )
253     {
254         MethodGuard aGuard( *this );
255 
256         (void)i_rNewLayouter;
257         m_rAntiImpl.NotifyAccessibleEvent( AccessibleEventId::INVALIDATE_ALL_CHILDREN, Any(), Any() );
258     }
259 
260 	//------------------------------------------------------------------------------------------------------------------
261     void AccessibleToolPanelDeck_Impl::Dying()
262     {
263         // the tool panel deck is dying, so dispose ourself
264         m_rAntiImpl.dispose();
265     }
266 
267 	//==================================================================================================================
268 	//= AccessibleToolPanelDeck
269 	//==================================================================================================================
270 	//------------------------------------------------------------------------------------------------------------------
271     AccessibleToolPanelDeck::AccessibleToolPanelDeck( const Reference< XAccessible >& i_rAccessibleParent,
272             ::svt::ToolPanelDeck& i_rPanelDeck )
273         :AccessibleToolPanelDeck_Base( i_rPanelDeck.GetWindowPeer() )
274         ,m_pImpl( new AccessibleToolPanelDeck_Impl( *this, i_rAccessibleParent, i_rPanelDeck ) )
275     {
276     }
277 
278 	//------------------------------------------------------------------------------------------------------------------
279     AccessibleToolPanelDeck::~AccessibleToolPanelDeck()
280     {
281     }
282 
283 	//------------------------------------------------------------------------------------------------------------------
284 	sal_Int32 SAL_CALL AccessibleToolPanelDeck::getAccessibleChildCount(  ) throw (RuntimeException)
285     {
286         MethodGuard aGuard( *m_pImpl );
287 
288         sal_Int32 nChildCount( m_pImpl->m_pPanelDeck->GetLayouter()->GetAccessibleChildCount() );
289 
290         ::boost::optional< size_t > aActivePanel( m_pImpl->m_pPanelDeck->GetActivePanel() );
291         if ( !!aActivePanel )
292             return ++nChildCount;
293 
294         return nChildCount;
295     }
296 
297 	//------------------------------------------------------------------------------------------------------------------
298 	Reference< XAccessible > SAL_CALL AccessibleToolPanelDeck::getAccessibleChild( sal_Int32 i_nIndex ) throw (IndexOutOfBoundsException, RuntimeException)
299     {
300         MethodGuard aGuard( *m_pImpl );
301 
302         const sal_Int32 nChildCount( getAccessibleChildCount() );
303         if ( ( i_nIndex < 0 ) || ( i_nIndex >= nChildCount ) )
304             throw IndexOutOfBoundsException( ::rtl::OUString(), *this );
305 
306         // first "n" children are provided by the layouter
307         const size_t nLayouterCount( m_pImpl->m_pPanelDeck->GetLayouter()->GetAccessibleChildCount() );
308         if ( size_t( i_nIndex ) < nLayouterCount )
309             return m_pImpl->m_pPanelDeck->GetLayouter()->GetAccessibleChild(
310                 size_t( i_nIndex ),
311                 m_pImpl->getOwnAccessible()
312             );
313 
314         // the last child is the XAccessible of the active panel
315         return m_pImpl->getActivePanelAccessible();
316     }
317 
318 	//------------------------------------------------------------------------------------------------------------------
319 	Reference< XAccessible > SAL_CALL AccessibleToolPanelDeck::getAccessibleParent(  ) throw (RuntimeException)
320     {
321         MethodGuard aGuard( *m_pImpl );
322         const Reference< XAccessible > xParent = implGetForeignControlledParent();
323         if ( xParent.is() )
324             return xParent;
325         return m_pImpl->m_xAccessibleParent;
326     }
327 
328 	//------------------------------------------------------------------------------------------------------------------
329 	sal_Int16 SAL_CALL AccessibleToolPanelDeck::getAccessibleRole(  ) throw (RuntimeException)
330     {
331         MethodGuard aGuard( *m_pImpl );
332         return AccessibleRole::PANEL;
333     }
334 
335 	//------------------------------------------------------------------------------------------------------------------
336     Reference< XAccessible > SAL_CALL AccessibleToolPanelDeck::getAccessibleAtPoint( const UnoPoint& i_rPoint ) throw (RuntimeException)
337     {
338         MethodGuard aGuard( *m_pImpl );
339 
340         const ::Point aRequestedPoint( VCLUnoHelper::ConvertToVCLPoint( i_rPoint ) );
341         // check the panel window itself
342         const ::Window& rActivePanelAnchor( m_pImpl->m_pPanelDeck->GetPanelWindowAnchor() );
343         const Rectangle aPanelAnchorArea( rActivePanelAnchor.GetPosPixel(), rActivePanelAnchor.GetOutputSizePixel() );
344         if ( aPanelAnchorArea.IsInside( aRequestedPoint ) )
345             // note that this assumes that the Window which actually implements the concrete panel covers
346             // the complete area of its "anchor" Window. But this is ensured by the ToolPanelDeck implementation.
347             return m_pImpl->getActivePanelAccessible();
348 
349         // check the XAccessible instances provided by the layouter
350         try
351         {
352             const ::svt::PDeckLayouter pLayouter( m_pImpl->m_pPanelDeck->GetLayouter() );
353             ENSURE_OR_THROW( pLayouter.get() != NULL, "invalid layouter" );
354 
355             const size_t nLayouterChildren = pLayouter->GetAccessibleChildCount();
356             for ( size_t i=0; i<nLayouterChildren; ++i )
357             {
358                 const Reference< XAccessible > xLayoutItemAccessible( pLayouter->GetAccessibleChild( i, m_pImpl->getOwnAccessible() ), UNO_SET_THROW );
359                 const Reference< XAccessibleComponent > xLayoutItemComponent( xLayoutItemAccessible->getAccessibleContext(), UNO_QUERY_THROW );
360                 const ::Rectangle aLayoutItemBounds( VCLUnoHelper::ConvertToVCLRect( xLayoutItemComponent->getBounds() ) );
361                 if ( aLayoutItemBounds.IsInside( aRequestedPoint ) )
362                     return xLayoutItemAccessible;
363             }
364         }
365         catch( const Exception& )
366         {
367         	DBG_UNHANDLED_EXCEPTION();
368         }
369 
370         return NULL;
371     }
372 
373 	//------------------------------------------------------------------------------------------------------------------
374     void SAL_CALL AccessibleToolPanelDeck::grabFocus(  ) throw (RuntimeException)
375     {
376         MethodGuard aGuard( *m_pImpl );
377         m_pImpl->m_pPanelDeck->GrabFocus();
378     }
379 
380 	//------------------------------------------------------------------------------------------------------------------
381     void SAL_CALL AccessibleToolPanelDeck::disposing()
382     {
383         AccessibleToolPanelDeck_Base::disposing();
384         m_pImpl->dispose();
385     }
386 
387 	//------------------------------------------------------------------------------------------------------------------
388     Reference< XAccessible > AccessibleToolPanelDeck::GetChildAccessible( const VclWindowEvent& i_rVclWindowEvent )
389     {
390         // don't let the base class generate any A11Y events from VclWindowEvent, we completely manage those
391         // A11Y events ourself
392         (void)i_rVclWindowEvent;
393         return NULL;
394     }
395 
396 	//------------------------------------------------------------------------------------------------------------------
397     void AccessibleToolPanelDeck::FillAccessibleStateSet( ::utl::AccessibleStateSetHelper& i_rStateSet )
398     {
399         AccessibleToolPanelDeck_Base::FillAccessibleStateSet( i_rStateSet );
400         if ( m_pImpl->isDisposed() )
401         {
402             i_rStateSet.AddState( AccessibleStateType::DEFUNC );
403         }
404         else
405         {
406             i_rStateSet.AddState( AccessibleStateType::FOCUSABLE );
407         }
408     }
409 
410 //......................................................................................................................
411 } // namespace accessibility
412 //......................................................................................................................
413