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/AccessibleToolPanelDeckTabBar.hxx"
30 #include "accessibility/extended/AccessibleToolPanelDeckTabBarItem.hxx"
31 #include "accessibility/helper/accresmgr.hxx"
32 #include "accessibility/helper/accessiblestrings.hrc"
33 
34 /** === begin UNO includes === **/
35 #include <com/sun/star/accessibility/AccessibleRole.hpp>
36 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
37 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
38 #include <com/sun/star/lang/DisposedException.hpp>
39 /** === end UNO includes === **/
40 
41 #include <svtools/toolpanel/toolpaneldeck.hxx>
42 #include <svtools/toolpanel/paneltabbar.hxx>
43 #include <unotools/accessiblestatesethelper.hxx>
44 #include <toolkit/awt/vclxwindow.hxx>
45 #include <toolkit/helper/vclunohelper.hxx>
46 #include <vcl/svapp.hxx>
47 #include <vcl/button.hxx>
48 #include <vos/mutex.hxx>
49 #include <tools/diagnose_ex.h>
50 
51 #include <vector>
52 
53 //......................................................................................................................
54 namespace accessibility
55 {
56 //......................................................................................................................
57 
58 	/** === begin UNO using === **/
59 	using ::com::sun::star::uno::Reference;
60 	using ::com::sun::star::uno::XInterface;
61 	using ::com::sun::star::uno::UNO_QUERY;
62 	using ::com::sun::star::uno::UNO_QUERY_THROW;
63 	using ::com::sun::star::uno::UNO_SET_THROW;
64 	using ::com::sun::star::uno::Exception;
65 	using ::com::sun::star::uno::RuntimeException;
66 	using ::com::sun::star::uno::Any;
67 	using ::com::sun::star::uno::makeAny;
68 	using ::com::sun::star::uno::Sequence;
69 	using ::com::sun::star::uno::Type;
70     using ::com::sun::star::accessibility::XAccessible;
71     using ::com::sun::star::lang::DisposedException;
72     using ::com::sun::star::lang::IndexOutOfBoundsException;
73     using ::com::sun::star::accessibility::XAccessibleContext;
74     /** === end UNO using === **/
75 
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::Point        UnoPoint;
81     typedef ::com::sun::star::awt::Size         UnoSize;
82     typedef ::com::sun::star::awt::Rectangle    UnoRectangle;
83 
84 	//==================================================================================================================
85 	//= AccessibleWrapper
86 	//==================================================================================================================
87     typedef ::cppu::WeakImplHelper1< XAccessible > AccessibleWrapper_Base;
88     class AccessibleWrapper : public AccessibleWrapper_Base
89     {
90     public:
91         AccessibleWrapper( const Reference< XAccessibleContext >& i_rContext )
92             :m_xContext( i_rContext )
93         {
94         }
95 
96         // XAccessible
97         virtual Reference< XAccessibleContext > SAL_CALL getAccessibleContext(  ) throw (RuntimeException)
98         {
99             return m_xContext;
100         }
101 
102     private:
103         const Reference< XAccessibleContext >   m_xContext;
104     };
105 
106 	//==================================================================================================================
107 	//= AccessibleToolPanelTabBar_Impl
108 	//==================================================================================================================
109     class AccessibleToolPanelTabBar_Impl    :public ::boost::noncopyable
110                                             ,public ::svt::IToolPanelDeckListener
111     {
112     public:
113         AccessibleToolPanelTabBar_Impl(
114             AccessibleToolPanelTabBar& i_rAntiImpl,
115             const Reference< XAccessible >& i_rAccessibleParent,
116             ::svt::IToolPanelDeck& i_rPanelDeck,
117             ::svt::PanelTabBar& i_rTabBar
118         );
119         ~AccessibleToolPanelTabBar_Impl();
120 
121         void    checkDisposed();
122         bool    isDisposed() const { return m_pPanelDeck == NULL; }
123         void    dispose();
124 
125         ::svt::IToolPanelDeck*          getPanelDeck() const { return m_pPanelDeck; }
126         ::svt::PanelTabBar*             getTabBar() const { return m_pTabBar; }
127         const Reference< XAccessible >& getAccessibleParent() const { return m_xAccessibleParent; }
128         Reference< XAccessible >        getAccessiblePanelItem( size_t i_nPosition );
129         Reference< XAccessible >        getOwnAccessible() const;
130 
131     protected:
132         // IToolPanelDeckListener
133         virtual void PanelInserted( const ::svt::PToolPanel& i_pPanel, const size_t i_nPosition );
134         virtual void PanelRemoved( const size_t i_nPosition );
135         virtual void ActivePanelChanged( const ::boost::optional< size_t >& i_rOldActive, const ::boost::optional< size_t >& i_rNewActive );
136         virtual void LayouterChanged( const ::svt::PDeckLayouter& i_rNewLayouter );
137         virtual void Dying();
138 
139         DECL_LINK( OnWindowEvent, const VclSimpleEvent* );
140 
141     private:
142         AccessibleToolPanelTabBar&                  m_rAntiImpl;
143         Reference< XAccessible >                    m_xAccessibleParent;
144         ::svt::IToolPanelDeck*                      m_pPanelDeck;
145         ::svt::PanelTabBar*                         m_pTabBar;
146         ::std::vector< Reference< XAccessible > >   m_aChildren;
147     };
148 
149 	//------------------------------------------------------------------------------------------------------------------
150     AccessibleToolPanelTabBar_Impl::AccessibleToolPanelTabBar_Impl( AccessibleToolPanelTabBar& i_rAntiImpl,
151             const Reference< XAccessible >& i_rAccessibleParent, ::svt::IToolPanelDeck& i_rPanelDeck, ::svt::PanelTabBar& i_rTabBar )
152         :m_rAntiImpl( i_rAntiImpl )
153         ,m_xAccessibleParent( i_rAccessibleParent )
154         ,m_pPanelDeck( &i_rPanelDeck )
155         ,m_pTabBar( &i_rTabBar )
156         ,m_aChildren()
157     {
158         m_pPanelDeck->AddListener( *this );
159         m_aChildren.resize( m_pPanelDeck->GetPanelCount() );
160 
161         const String sAccessibleDescription( TK_RES_STRING( RID_STR_ACC_DESC_PANELDECL_TABBAR ) );
162         i_rTabBar.SetAccessibleName( sAccessibleDescription );
163         i_rTabBar.SetAccessibleDescription( sAccessibleDescription );
164 
165         i_rTabBar.GetScrollButton( true ).AddEventListener( LINK( this, AccessibleToolPanelTabBar_Impl, OnWindowEvent ) );
166         i_rTabBar.GetScrollButton( false ).AddEventListener( LINK( this, AccessibleToolPanelTabBar_Impl, OnWindowEvent ) );
167     }
168 
169 	//------------------------------------------------------------------------------------------------------------------
170     void AccessibleToolPanelTabBar_Impl::checkDisposed()
171     {
172         if ( isDisposed() )
173             throw DisposedException( ::rtl::OUString(), *&m_rAntiImpl );
174     }
175 
176 	//------------------------------------------------------------------------------------------------------------------
177     AccessibleToolPanelTabBar_Impl::~AccessibleToolPanelTabBar_Impl()
178     {
179         if ( !isDisposed() )
180             dispose();
181     }
182 
183 	//------------------------------------------------------------------------------------------------------------------
184     void AccessibleToolPanelTabBar_Impl::dispose()
185     {
186         ENSURE_OR_RETURN_VOID( !isDisposed(), "disposed twice" );
187         m_pPanelDeck->RemoveListener( *this );
188         m_pPanelDeck = NULL;
189 
190         m_pTabBar->GetScrollButton( true ).RemoveEventListener( LINK( this, AccessibleToolPanelTabBar_Impl, OnWindowEvent ) );
191         m_pTabBar->GetScrollButton( false ).RemoveEventListener( LINK( this, AccessibleToolPanelTabBar_Impl, OnWindowEvent ) );
192         m_pTabBar = NULL;
193 
194         m_xAccessibleParent.clear();
195     }
196 
197 	//------------------------------------------------------------------------------------------------------------------
198     Reference< XAccessible > AccessibleToolPanelTabBar_Impl::getAccessiblePanelItem( size_t i_nPosition )
199     {
200         ENSURE_OR_RETURN( !isDisposed(), "AccessibleToolPanelTabBar_Impl::getAccessiblePanelItem: already disposed!", NULL );
201         ENSURE_OR_RETURN( i_nPosition < m_aChildren.size(), "AccessibleToolPanelTabBar_Impl::getAccessiblePanelItem: invalid index!", NULL );
202 
203         Reference< XAccessible >& rAccessibleChild( m_aChildren[ i_nPosition ] );
204         if ( !rAccessibleChild.is() )
205         {
206             ::rtl::Reference< AccessibleToolPanelDeckTabBarItem > pAccesibleItemContext( new AccessibleToolPanelDeckTabBarItem(
207                 getOwnAccessible(), *m_pPanelDeck, *m_pTabBar, i_nPosition ) );
208             rAccessibleChild.set( new AccessibleWrapper( pAccesibleItemContext.get() ) );
209             pAccesibleItemContext->lateInit( rAccessibleChild );
210         }
211         return rAccessibleChild;
212     }
213 
214 	//------------------------------------------------------------------------------------------------------------------
215     Reference< XAccessible > AccessibleToolPanelTabBar_Impl::getOwnAccessible() const
216     {
217         Reference< XAccessible > xOwnAccessible( static_cast< XAccessible* >( m_rAntiImpl.GetVCLXWindow() ) );
218         OSL_ENSURE( xOwnAccessible->getAccessibleContext() == Reference< XAccessibleContext >( &m_rAntiImpl ),
219             "AccessibleToolPanelTabBar_Impl::getOwnAccessible: could not retrieve proper XAccessible for /myself!" );
220         return xOwnAccessible;
221     }
222 
223 	//------------------------------------------------------------------------------------------------------------------
224     void AccessibleToolPanelTabBar_Impl::PanelInserted( const ::svt::PToolPanel& i_pPanel, const size_t i_nPosition )
225     {
226         ENSURE_OR_RETURN_VOID( i_nPosition <= m_aChildren.size(), "AccessibleToolPanelTabBar_Impl::PanelInserted: illegal position (or invalid cache!)" );
227         (void)i_pPanel;
228         m_aChildren.insert( m_aChildren.begin() + i_nPosition, NULL );
229         m_rAntiImpl.NotifyAccessibleEvent( AccessibleEventId::CHILD, Any(), makeAny( getAccessiblePanelItem( i_nPosition ) ) );
230     }
231 
232 	//------------------------------------------------------------------------------------------------------------------
233     void AccessibleToolPanelTabBar_Impl::PanelRemoved( const size_t i_nPosition )
234     {
235         ENSURE_OR_RETURN_VOID( i_nPosition < m_aChildren.size(), "AccessibleToolPanelTabBar_Impl::PanelInserted: illegal position (or invalid cache!)" );
236 
237         const Reference< XAccessible > xOldChild( getAccessiblePanelItem( i_nPosition ) );
238         m_aChildren.erase( m_aChildren.begin() + i_nPosition );
239         m_rAntiImpl.NotifyAccessibleEvent( AccessibleEventId::CHILD, makeAny( xOldChild ), Any() );
240     }
241 
242 	//------------------------------------------------------------------------------------------------------------------
243     void AccessibleToolPanelTabBar_Impl::ActivePanelChanged( const ::boost::optional< size_t >& i_rOldActive, const ::boost::optional< size_t >& i_rNewActive )
244     {
245         (void)i_rOldActive;
246         (void)i_rNewActive;
247     }
248 
249 	//------------------------------------------------------------------------------------------------------------------
250     void AccessibleToolPanelTabBar_Impl::LayouterChanged( const ::svt::PDeckLayouter& i_rNewLayouter )
251     {
252         (void)i_rNewLayouter;
253         m_rAntiImpl.dispose();
254     }
255 
256 	//------------------------------------------------------------------------------------------------------------------
257     void AccessibleToolPanelTabBar_Impl::Dying()
258     {
259         m_rAntiImpl.dispose();
260     }
261 
262 	//------------------------------------------------------------------------------------------------------------------
263     IMPL_LINK( AccessibleToolPanelTabBar_Impl, OnWindowEvent, const VclSimpleEvent*, i_pEvent )
264     {
265         ENSURE_OR_RETURN( !isDisposed(), "AccessibleToolPanelTabBar_Impl::OnWindowEvent: already disposed!", 0L );
266 
267         const VclWindowEvent* pWindowEvent( dynamic_cast< const VclWindowEvent* >( i_pEvent ) );
268         if ( !pWindowEvent )
269             return 0L;
270 
271         const bool bForwardButton = ( pWindowEvent->GetWindow() == &m_pTabBar->GetScrollButton( true ) );
272         const bool bBackwardButton = ( pWindowEvent->GetWindow() == &m_pTabBar->GetScrollButton( false ) );
273         ENSURE_OR_RETURN( bForwardButton || bBackwardButton, "AccessibleToolPanelTabBar_Impl::OnWindowEvent: where does this come from?", 0L );
274 
275         const bool bShow = ( i_pEvent->GetId() == VCLEVENT_WINDOW_SHOW );
276         const bool bHide = ( i_pEvent->GetId() == VCLEVENT_WINDOW_HIDE );
277         if ( !bShow && !bHide )
278             // not interested in events other than visibility changes
279             return 0L;
280 
281         const Reference< XAccessible > xButtonAccessible( m_pTabBar->GetScrollButton( bForwardButton ).GetAccessible() );
282         const Any aOldChild( bHide ? xButtonAccessible : Reference< XAccessible >() );
283         const Any aNewChild( bShow ? xButtonAccessible : Reference< XAccessible >() );
284         m_rAntiImpl.NotifyAccessibleEvent( AccessibleEventId::CHILD, aOldChild, aNewChild );
285 
286         return 1L;
287     }
288 
289 	//==================================================================================================================
290 	//= MethodGuard
291 	//==================================================================================================================
292     namespace
293     {
294         class MethodGuard
295         {
296         public:
297             MethodGuard( AccessibleToolPanelTabBar_Impl& i_rImpl )
298                 :m_aGuard( Application::GetSolarMutex() )
299             {
300                 i_rImpl.checkDisposed();
301             }
302             ~MethodGuard()
303             {
304             }
305 
306             void clear()
307             {
308                 m_aGuard.clear();
309             }
310 
311         private:
312             ::vos::OClearableGuard  m_aGuard;
313         };
314     }
315 
316 	//==================================================================================================================
317 	//= AccessibleToolPanelTabBar
318 	//==================================================================================================================
319 	//------------------------------------------------------------------------------------------------------------------
320     AccessibleToolPanelTabBar::AccessibleToolPanelTabBar( const Reference< XAccessible >& i_rAccessibleParent,
321             ::svt::IToolPanelDeck& i_rPanelDeck, ::svt::PanelTabBar& i_rTabBar )
322         :AccessibleToolPanelTabBar_Base( i_rTabBar.GetWindowPeer() )
323         ,m_pImpl( new AccessibleToolPanelTabBar_Impl( *this, i_rAccessibleParent, i_rPanelDeck, i_rTabBar ) )
324     {
325     }
326 
327 	//------------------------------------------------------------------------------------------------------------------
328     AccessibleToolPanelTabBar::~AccessibleToolPanelTabBar()
329     {
330     }
331 
332 	//------------------------------------------------------------------------------------------------------------------
333 	sal_Int32 SAL_CALL AccessibleToolPanelTabBar::getAccessibleChildCount(  ) throw (RuntimeException)
334     {
335         MethodGuard aGuard( *m_pImpl );
336 
337         const bool bHasScrollBack = m_pImpl->getTabBar()->GetScrollButton( false ).IsVisible();
338         const bool bHasScrollForward = m_pImpl->getTabBar()->GetScrollButton( true ).IsVisible();
339 
340         return  m_pImpl->getPanelDeck()->GetPanelCount()
341             +   ( bHasScrollBack ? 1 : 0 )
342             +   ( bHasScrollForward ? 1 : 0 );
343     }
344 
345 	//------------------------------------------------------------------------------------------------------------------
346 	Reference< XAccessible > SAL_CALL AccessibleToolPanelTabBar::getAccessibleChild( sal_Int32 i_nIndex ) throw (IndexOutOfBoundsException, RuntimeException)
347     {
348         MethodGuard aGuard( *m_pImpl );
349 
350         const bool bHasScrollBack = m_pImpl->getTabBar()->GetScrollButton( false ).IsVisible();
351         const bool bHasScrollForward = m_pImpl->getTabBar()->GetScrollButton( true ).IsVisible();
352 
353         const bool bScrollBackRequested = ( bHasScrollBack && ( i_nIndex == 0 ) );
354         const bool bScrollForwardRequested = ( bHasScrollForward && ( i_nIndex == getAccessibleChildCount() - 1 ) );
355         OSL_ENSURE( !( bScrollBackRequested && bScrollForwardRequested ), "AccessibleToolPanelTabBar::getAccessibleChild: ouch!" );
356 
357         if ( bScrollBackRequested || bScrollForwardRequested )
358         {
359             Reference< XAccessible > xScrollButtonAccessible( m_pImpl->getTabBar()->GetScrollButton( bScrollForwardRequested ).GetAccessible() );
360             ENSURE_OR_RETURN( xScrollButtonAccessible.is(), "AccessibleToolPanelTabBar::getAccessibleChild: invalid button accessible!", NULL );
361         #if OSL_DEBUG_LEVEL > 0
362             Reference< XAccessibleContext > xScrollButtonContext( xScrollButtonAccessible->getAccessibleContext() );
363             ENSURE_OR_RETURN( xScrollButtonContext.is(), "AccessibleToolPanelTabBar::getAccessibleChild: invalid button accessible context!", xScrollButtonAccessible );
364             OSL_ENSURE( xScrollButtonContext->getAccessibleParent() == m_pImpl->getOwnAccessible(),
365                 "AccessibleToolPanelTabBar::getAccessibleChild: wrong parent at the button's accesible!" );
366         #endif
367             return xScrollButtonAccessible;
368         }
369 
370         return m_pImpl->getAccessiblePanelItem( i_nIndex - ( bHasScrollBack ? 1 : 0 ) );
371     }
372 
373 	//------------------------------------------------------------------------------------------------------------------
374 	Reference< XAccessible > SAL_CALL AccessibleToolPanelTabBar::getAccessibleParent(  ) throw (RuntimeException)
375     {
376         MethodGuard aGuard( *m_pImpl );
377         return m_pImpl->getAccessibleParent();
378     }
379 
380 	//------------------------------------------------------------------------------------------------------------------
381 	sal_Int16 SAL_CALL AccessibleToolPanelTabBar::getAccessibleRole(  ) throw (RuntimeException)
382     {
383         MethodGuard aGuard( *m_pImpl );
384         return AccessibleRole::PAGE_TAB_LIST;
385     }
386 
387 	//------------------------------------------------------------------------------------------------------------------
388     namespace
389     {
390         bool lcl_covers( const ::Window& i_rWindow, const ::Point& i_rPoint )
391         {
392             const Rectangle aWindowBounds( i_rWindow.GetWindowExtentsRelative( i_rWindow.GetParent() ) );
393             return aWindowBounds.IsInside( i_rPoint );
394         }
395     }
396 
397 	//------------------------------------------------------------------------------------------------------------------
398 	Reference< XAccessible > SAL_CALL AccessibleToolPanelTabBar::getAccessibleAtPoint( const UnoPoint& i_rPoint ) throw (RuntimeException)
399     {
400         MethodGuard aGuard( *m_pImpl );
401 
402         // check the tab items
403         const UnoPoint aOwnScreenPos( getLocationOnScreen() );
404         const ::Point aRequestedScreenPoint( i_rPoint.X + aOwnScreenPos.X, i_rPoint.Y + aOwnScreenPos.Y );
405 
406         for ( size_t i=0; i<m_pImpl->getPanelDeck()->GetPanelCount(); ++i )
407         {
408             const ::Rectangle aItemScreenRect( m_pImpl->getTabBar()->GetItemScreenRect(i) );
409             if ( aItemScreenRect.IsInside( aRequestedScreenPoint ) )
410                 return m_pImpl->getAccessiblePanelItem(i);
411         }
412 
413         // check the scroll buttons
414         const ::Point aRequestedClientPoint( VCLUnoHelper::ConvertToVCLPoint( i_rPoint ) );
415 
416         const bool bHasScrollBack = m_pImpl->getTabBar()->GetScrollButton( false ).IsVisible();
417         if ( bHasScrollBack && lcl_covers( m_pImpl->getTabBar()->GetScrollButton( false ), aRequestedClientPoint ) )
418             return m_pImpl->getTabBar()->GetScrollButton( false ).GetAccessible();
419 
420         const bool bHasScrollForward = m_pImpl->getTabBar()->GetScrollButton( true ).IsVisible();
421         if ( bHasScrollForward && lcl_covers( m_pImpl->getTabBar()->GetScrollButton( true ), aRequestedClientPoint ) )
422             return m_pImpl->getTabBar()->GetScrollButton( true ).GetAccessible();
423 
424         // no hit
425         return NULL;
426     }
427 
428 	//------------------------------------------------------------------------------------------------------------------
429     void SAL_CALL AccessibleToolPanelTabBar::disposing()
430     {
431         AccessibleToolPanelTabBar_Base::disposing();
432         m_pImpl->dispose();
433     }
434 
435 	//------------------------------------------------------------------------------------------------------------------
436     Reference< XAccessible > AccessibleToolPanelTabBar::GetChildAccessible( const VclWindowEvent& i_rVclWindowEvent )
437     {
438         // don't let the base class generate any A11Y events from VclWindowEvent, we completely manage those
439         // A11Y events ourself
440         (void)i_rVclWindowEvent;
441         return NULL;
442     }
443 
444 	//------------------------------------------------------------------------------------------------------------------
445     void AccessibleToolPanelTabBar::FillAccessibleStateSet( ::utl::AccessibleStateSetHelper& i_rStateSet )
446     {
447         AccessibleToolPanelTabBar_Base::FillAccessibleStateSet( i_rStateSet );
448         i_rStateSet.AddState( AccessibleStateType::FOCUSABLE );
449 
450         ENSURE_OR_RETURN_VOID( !m_pImpl->isDisposed(), "AccessibleToolPanelTabBar::FillAccessibleStateSet: already disposed!" );
451         if ( m_pImpl->getTabBar()->IsVertical() )
452             i_rStateSet.AddState( AccessibleStateType::VERTICAL );
453         else
454             i_rStateSet.AddState( AccessibleStateType::HORIZONTAL );
455     }
456 
457 //......................................................................................................................
458 } // namespace accessibility
459 //......................................................................................................................
460