1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_accessibility.hxx"
26 #include <accessibility/standard/accessiblemenuitemcomponent.hxx>
27 
28 
29 #include <accessibility/helper/accresmgr.hxx>
30 #include <accessibility/helper/accessiblestrings.hrc>
31 #include <toolkit/awt/vclxwindows.hxx>
32 #include <toolkit/helper/externallock.hxx>
33 #include <toolkit/helper/convert.hxx>
34 
35 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
36 #include <com/sun/star/accessibility/AccessibleRole.hpp>
37 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
38 #include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
39 #include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp>
40 
41 #include <unotools/accessiblestatesethelper.hxx>
42 #include <unotools/accessiblerelationsethelper.hxx>
43 #include <cppuhelper/typeprovider.hxx>
44 #include <comphelper/sequence.hxx>
45 #include <comphelper/accessibletexthelper.hxx>
46 #include <vcl/svapp.hxx>
47 #include <vcl/window.hxx>
48 #include <vcl/menu.hxx>
49 #include <vcl/unohelp2.hxx>
50 
51 
52 using namespace ::com::sun::star::accessibility;
53 using namespace ::com::sun::star::uno;
54 using namespace ::com::sun::star::beans;
55 using namespace ::com::sun::star::lang;
56 using namespace ::com::sun::star;
57 using namespace ::comphelper;
58 
59 
60 // -----------------------------------------------------------------------------
61 // class OAccessibleMenuItemComponent
62 // -----------------------------------------------------------------------------
63 
64 OAccessibleMenuItemComponent::OAccessibleMenuItemComponent( Menu* pParent, sal_uInt16 nItemPos, Menu* pMenu )
65 	:OAccessibleMenuBaseComponent( pMenu )
66 	,m_pParent( pParent )
67 	,m_nItemPos( nItemPos )
68 {
69 	m_sAccessibleName = GetAccessibleName();
70 	m_sItemText = GetItemText();
71 }
72 
73 // -----------------------------------------------------------------------------
74 
75 OAccessibleMenuItemComponent::~OAccessibleMenuItemComponent()
76 {
77 }
78 
79 // -----------------------------------------------------------------------------
80 
81 sal_Bool OAccessibleMenuItemComponent::IsEnabled()
82 {
83 	OExternalLockGuard aGuard( this );
84 
85 	sal_Bool bEnabled = sal_False;
86 	if ( m_pParent )
87 		bEnabled = m_pParent->IsItemEnabled( m_pParent->GetItemId( m_nItemPos ) );
88 
89 	return bEnabled;
90 }
91 
92 // -----------------------------------------------------------------------------
93 
94 sal_Bool OAccessibleMenuItemComponent::IsVisible()
95 {
96     sal_Bool bVisible = sal_False;
97 
98 	if ( m_pParent )
99         bVisible = m_pParent->IsItemPosVisible( m_nItemPos );
100 
101     return bVisible;
102 }
103 
104 // -----------------------------------------------------------------------------
105 
106 void OAccessibleMenuItemComponent::Select()
107 {
108 	// open the parent menu
109 	Reference< XAccessible > xParent( getAccessibleParent() );
110 	if ( xParent.is() )
111 	{
112 		OAccessibleMenuBaseComponent* pComp = static_cast< OAccessibleMenuBaseComponent* >( xParent.get() );
113 		if ( pComp && pComp->getAccessibleRole() == AccessibleRole::MENU && !pComp->IsPopupMenuOpen() )
114 			pComp->Click();
115 	}
116 
117 	// highlight the menu item
118 	if ( m_pParent )
119         m_pParent->HighlightItem( m_nItemPos );
120 }
121 
122 // -----------------------------------------------------------------------------
123 
124 void OAccessibleMenuItemComponent::DeSelect()
125 {
126 	if ( m_pParent && IsSelected() )
127 		m_pParent->DeHighlight();
128 }
129 
130 // -----------------------------------------------------------------------------
131 
132 void OAccessibleMenuItemComponent::Click()
133 {
134     // open the parent menu
135     Reference< XAccessible > xParent( getAccessibleParent() );
136     if ( xParent.is() )
137     {
138         OAccessibleMenuBaseComponent* pComp = static_cast< OAccessibleMenuBaseComponent* >( xParent.get() );
139         if ( pComp && pComp->getAccessibleRole() == AccessibleRole::MENU && !pComp->IsPopupMenuOpen() )
140             pComp->Click();
141     }
142 
143     // click the menu item
144     if ( m_pParent )
145     {
146         Window* pWindow = m_pParent->GetWindow();
147         if ( pWindow )
148         {
149             // #102438# Menu items are not selectable
150             // Popup menus are executed asynchronously, triggered by a timer.
151             // As Menu::SelectItem only works, if the corresponding menu window is
152             // already created, we have to set the menu delay to 0, so
153             // that the popup menus are executed synchronously.
154             AllSettings aSettings = pWindow->GetSettings();
155             MouseSettings aMouseSettings = aSettings.GetMouseSettings();
156             sal_uLong nDelay = aMouseSettings.GetMenuDelay();
157             aMouseSettings.SetMenuDelay( 0 );
158             aSettings.SetMouseSettings( aMouseSettings );
159             pWindow->SetSettings( aSettings );
160 
161             m_pParent->SelectItem( m_pParent->GetItemId( m_nItemPos ) );
162 
163             // meanwhile the window pointer may be invalid
164             pWindow = m_pParent->GetWindow();
165             if ( pWindow )
166             {
167                 // set the menu delay back to the old value
168                 aSettings = pWindow->GetSettings();
169                 aMouseSettings = aSettings.GetMouseSettings();
170                 aMouseSettings.SetMenuDelay( nDelay );
171                 aSettings.SetMouseSettings( aMouseSettings );
172                 pWindow->SetSettings( aSettings );
173             }
174         }
175     }
176 }
177 
178 // -----------------------------------------------------------------------------
179 
180 void OAccessibleMenuItemComponent::SetItemPos( sal_uInt16 nItemPos )
181 {
182 	m_nItemPos = nItemPos;
183 }
184 
185 // -----------------------------------------------------------------------------
186 
187 void OAccessibleMenuItemComponent::SetAccessibleName( const ::rtl::OUString& sAccessibleName )
188 {
189     if ( !m_sAccessibleName.equals( sAccessibleName ) )
190     {
191         Any aOldValue, aNewValue;
192         aOldValue <<= m_sAccessibleName;
193         aNewValue <<= sAccessibleName;
194         m_sAccessibleName = sAccessibleName;
195         NotifyAccessibleEvent( AccessibleEventId::NAME_CHANGED, aOldValue, aNewValue );
196     }
197 }
198 
199 // -----------------------------------------------------------------------------
200 
201 ::rtl::OUString OAccessibleMenuItemComponent::GetAccessibleName()
202 {
203 	::rtl::OUString sName;
204 	if ( m_pParent )
205 	{
206 		sal_uInt16 nItemId = m_pParent->GetItemId( m_nItemPos );
207 		sName = m_pParent->GetAccessibleName( nItemId );
208 		if ( sName.getLength() == 0 )
209 			sName = m_pParent->GetItemText( nItemId );
210 		sName = OutputDevice::GetNonMnemonicString( sName );
211 
212 		// IA2 CWS, MT: Is adding 5 blanks really before the accelname reasonable? And which Platform / Accessibility API does need it this way? ATK has API for this...
213 		// Also, IAccessible2 has IAccessibleAction::keyBinding, so I doubt that this is needed.
214 		// But if so, it needs to move to the IA2 bridge.
215 		/*
216 		::rtl::OUString sAccName = m_pParent->GetAccelKey( nItemId ).GetName();
217 		if ( sAccName.getLength() )
218 		{
219 			sName += ::rtl::OUString::createFromAscii("     ");
220 	    	sName += aAccelName;
221 		}
222 		*/
223 	}
224 
225 	return sName;
226 }
227 
228 // -----------------------------------------------------------------------------
229 
230 void OAccessibleMenuItemComponent::SetItemText( const ::rtl::OUString& sItemText )
231 {
232     Any aOldValue, aNewValue;
233     if ( OCommonAccessibleText::implInitTextChangedEvent( m_sItemText, sItemText, aOldValue, aNewValue ) )
234     {
235         m_sItemText = sItemText;
236         NotifyAccessibleEvent( AccessibleEventId::TEXT_CHANGED, aOldValue, aNewValue );
237     }
238 }
239 
240 // -----------------------------------------------------------------------------
241 
242 ::rtl::OUString OAccessibleMenuItemComponent::GetItemText()
243 {
244 	::rtl::OUString sText;
245 	if ( m_pParent )
246 		sText = OutputDevice::GetNonMnemonicString( m_pParent->GetItemText( m_pParent->GetItemId( m_nItemPos ) ) );
247 
248 	return sText;
249 }
250 
251 // -----------------------------------------------------------------------------
252 
253 void OAccessibleMenuItemComponent::FillAccessibleStateSet( utl::AccessibleStateSetHelper& rStateSet )
254 {
255 	sal_Bool bEnabled = IsEnabled();
256 	if ( bEnabled )
257     {
258         rStateSet.AddState( AccessibleStateType::ENABLED );
259         rStateSet.AddState( AccessibleStateType::SENSITIVE );
260     }
261 
262     if ( IsVisible() )
263 	{
264 		rStateSet.AddState( AccessibleStateType::SHOWING );
265 		if( !IsMenuHideDisabledEntries() || bEnabled )
266 			rStateSet.AddState( AccessibleStateType::VISIBLE );
267 	}
268     rStateSet.AddState( AccessibleStateType::OPAQUE );
269 }
270 
271 // -----------------------------------------------------------------------------
272 // OCommonAccessibleComponent
273 // -----------------------------------------------------------------------------
274 
275 awt::Rectangle OAccessibleMenuItemComponent::implGetBounds() throw (RuntimeException)
276 {
277 	awt::Rectangle aBounds( 0, 0, 0, 0 );
278 
279 	if ( m_pParent )
280 	{
281 		// get bounding rectangle of the item relative to the containing window
282 		aBounds = AWTRectangle( m_pParent->GetBoundingRectangle( m_nItemPos ) );
283 
284 		// get position of containing window in screen coordinates
285 		Window* pWindow = m_pParent->GetWindow();
286 		if ( pWindow )
287 		{
288 			Rectangle aRect = pWindow->GetWindowExtentsRelative( NULL );
289 			awt::Point aWindowScreenLoc = AWTPoint( aRect.TopLeft() );
290 
291 			// get position of accessible parent in screen coordinates
292 			Reference< XAccessible > xParent = getAccessibleParent();
293 			if ( xParent.is() )
294 			{
295 				Reference< XAccessibleComponent > xParentComponent( xParent->getAccessibleContext(), UNO_QUERY );
296 				if ( xParentComponent.is() )
297 				{
298 					awt::Point aParentScreenLoc = xParentComponent->getLocationOnScreen();
299 
300 					// calculate bounding rectangle of the item relative to the accessible parent
301 					aBounds.X += aWindowScreenLoc.X - aParentScreenLoc.X;
302 					aBounds.Y += aWindowScreenLoc.Y - aParentScreenLoc.Y;
303 				}
304 			}
305 		}
306 	}
307 
308 	return aBounds;
309 }
310 
311 // -----------------------------------------------------------------------------
312 // XComponent
313 // -----------------------------------------------------------------------------
314 
315 void SAL_CALL OAccessibleMenuItemComponent::disposing()
316 {
317 	OAccessibleMenuBaseComponent::disposing();
318 
319 	m_pParent = NULL;
320 	m_sAccessibleName = ::rtl::OUString();
321 	m_sItemText = ::rtl::OUString();
322 }
323 
324 // -----------------------------------------------------------------------------
325 // XAccessibleContext
326 // -----------------------------------------------------------------------------
327 
328 sal_Int32 OAccessibleMenuItemComponent::getAccessibleChildCount() throw (RuntimeException)
329 {
330 	OExternalLockGuard aGuard( this );
331 
332 	return 0;
333 }
334 
335 // -----------------------------------------------------------------------------
336 
337 Reference< XAccessible > OAccessibleMenuItemComponent::getAccessibleChild( sal_Int32 i ) throw (IndexOutOfBoundsException, RuntimeException)
338 {
339 	OExternalLockGuard aGuard( this );
340 
341 	if ( i < 0 || i >= getAccessibleChildCount() )
342 		throw IndexOutOfBoundsException();
343 
344 	return Reference< XAccessible >();
345 }
346 
347 // -----------------------------------------------------------------------------
348 
349 Reference< XAccessible > OAccessibleMenuItemComponent::getAccessibleParent(  ) throw (RuntimeException)
350 {
351 	OExternalLockGuard aGuard( this );
352 
353 	return m_pParent->GetAccessible();
354 }
355 
356 // -----------------------------------------------------------------------------
357 
358 sal_Int32 OAccessibleMenuItemComponent::getAccessibleIndexInParent(  ) throw (RuntimeException)
359 {
360 	OExternalLockGuard aGuard( this );
361 
362 	return m_nItemPos;
363 }
364 
365 // -----------------------------------------------------------------------------
366 
367 sal_Int16 OAccessibleMenuItemComponent::getAccessibleRole(  ) throw (RuntimeException)
368 {
369 	OExternalLockGuard aGuard( this );
370 
371 	return AccessibleRole::UNKNOWN;
372 }
373 
374 // -----------------------------------------------------------------------------
375 
376 ::rtl::OUString OAccessibleMenuItemComponent::getAccessibleDescription(	) throw (RuntimeException)
377 {
378 	OExternalLockGuard aGuard( this );
379 
380 	::rtl::OUString sDescription;
381 	if ( m_pParent )
382 		sDescription = m_pParent->GetHelpText( m_pParent->GetItemId( m_nItemPos ) );
383 
384 	return sDescription;
385 }
386 
387 // -----------------------------------------------------------------------------
388 
389 ::rtl::OUString OAccessibleMenuItemComponent::getAccessibleName(  ) throw (RuntimeException)
390 {
391 	OExternalLockGuard aGuard( this );
392 
393 	return m_sAccessibleName;
394 }
395 
396 // -----------------------------------------------------------------------------
397 
398 Reference< XAccessibleRelationSet > OAccessibleMenuItemComponent::getAccessibleRelationSet(  ) throw (RuntimeException)
399 {
400 	OExternalLockGuard aGuard( this );
401 
402     utl::AccessibleRelationSetHelper* pRelationSetHelper = new utl::AccessibleRelationSetHelper;
403 	Reference< XAccessibleRelationSet > xSet = pRelationSetHelper;
404     return xSet;
405 }
406 
407 // -----------------------------------------------------------------------------
408 
409 Locale OAccessibleMenuItemComponent::getLocale(  ) throw (IllegalAccessibleComponentStateException, RuntimeException)
410 {
411 	OExternalLockGuard aGuard( this );
412 
413 	return Application::GetSettings().GetLocale();
414 }
415 
416 // -----------------------------------------------------------------------------
417 // XAccessibleComponent
418 // -----------------------------------------------------------------------------
419 
420 Reference< XAccessible > OAccessibleMenuItemComponent::getAccessibleAtPoint( const awt::Point& ) throw (RuntimeException)
421 {
422 	OExternalLockGuard aGuard( this );
423 
424 	return Reference< XAccessible >();
425 }
426 
427 // -----------------------------------------------------------------------------
428 
429 void OAccessibleMenuItemComponent::grabFocus(  ) throw (RuntimeException)
430 {
431 	// no focus for items
432 }
433 
434 // -----------------------------------------------------------------------------
435 
436 sal_Int32 OAccessibleMenuItemComponent::getForeground(	) throw (RuntimeException)
437 {
438 	OExternalLockGuard aGuard( this );
439 
440 	sal_Int32 nColor = 0;
441 	Reference< XAccessible > xParent = getAccessibleParent();
442 	if ( xParent.is() )
443 	{
444 		Reference< XAccessibleComponent > xParentComp( xParent->getAccessibleContext(), UNO_QUERY );
445 		if ( xParentComp.is() )
446 			nColor = xParentComp->getForeground();
447 	}
448 
449 	return nColor;
450 }
451 
452 // -----------------------------------------------------------------------------
453 
454 sal_Int32 OAccessibleMenuItemComponent::getBackground(  ) throw (RuntimeException)
455 {
456 	OExternalLockGuard aGuard( this );
457 
458 	sal_Int32 nColor = 0;
459 	Reference< XAccessible > xParent = getAccessibleParent();
460 	if ( xParent.is() )
461 	{
462 		Reference< XAccessibleComponent > xParentComp( xParent->getAccessibleContext(), UNO_QUERY );
463 		if ( xParentComp.is() )
464 			nColor = xParentComp->getBackground();
465 	}
466 
467 	return nColor;
468 }
469 
470 // -----------------------------------------------------------------------------
471 // XAccessibleExtendedComponent
472 // -----------------------------------------------------------------------------
473 
474 Reference< awt::XFont > OAccessibleMenuItemComponent::getFont(  ) throw (RuntimeException)
475 {
476 	OExternalLockGuard aGuard( this );
477 
478 	Reference< awt::XFont > xFont;
479 	Reference< XAccessible > xParent = getAccessibleParent();
480 	if ( xParent.is() )
481 	{
482 		Reference< XAccessibleExtendedComponent > xParentComp( xParent->getAccessibleContext(), UNO_QUERY );
483 		if ( xParentComp.is() )
484 			xFont = xParentComp->getFont();
485 	}
486 
487 	return xFont;
488 }
489 
490 // -----------------------------------------------------------------------------
491 
492 ::rtl::OUString OAccessibleMenuItemComponent::getTitledBorderText(  ) throw (RuntimeException)
493 {
494 	OExternalLockGuard aGuard( this );
495 
496 	return ::rtl::OUString();
497 }
498 
499 // -----------------------------------------------------------------------------
500 
501 ::rtl::OUString OAccessibleMenuItemComponent::getToolTipText(  ) throw (RuntimeException)
502 {
503 	OExternalLockGuard aGuard( this );
504 
505 	::rtl::OUString sRet;
506 	if ( m_pParent )
507 		sRet = m_pParent->GetTipHelpText( m_pParent->GetItemId( m_nItemPos ) );
508 
509 	return sRet;
510 }
511 
512 // -----------------------------------------------------------------------------
513 
514 sal_Bool OAccessibleMenuItemComponent::IsMenuHideDisabledEntries()
515 {
516 	if (m_pParent )
517 	{
518 		if( m_pParent->GetMenuFlags() & MENU_FLAG_HIDEDISABLEDENTRIES)
519 		{
520 			return sal_True;
521 		}
522 		// IA2 CWS, but the menus shouldn't have different flags, and even if so, the GetStartedFromMenu shouldn't matter
523 		/*
524 		else if (m_pParent->GetStartedFromMenu() &&
525 				m_pParent->GetStartedFromMenu()->GetMenuFlags() & MENU_FLAG_HIDEDISABLEDENTRIES)
526 		{
527 			return sal_True;
528 		}
529 		*/
530 	}
531 	return sal_False;
532 }
533