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/vclxaccessiblebox.hxx> 27 #include <accessibility/standard/vclxaccessibletextfield.hxx> 28 #include <accessibility/standard/vclxaccessibleedit.hxx> 29 #include <accessibility/standard/vclxaccessiblelist.hxx> 30 #include <accessibility/helper/listboxhelper.hxx> 31 32 #include <unotools/accessiblestatesethelper.hxx> 33 #include <com/sun/star/accessibility/AccessibleStateType.hpp> 34 #include <com/sun/star/accessibility/AccessibleEventId.hpp> 35 #include <com/sun/star/accessibility/AccessibleRole.hpp> 36 #include <vcl/svapp.hxx> 37 #include <vcl/combobox.hxx> 38 #include <vcl/lstbox.hxx> 39 #include <accessibility/helper/accresmgr.hxx> 40 #include <accessibility/helper/accessiblestrings.hrc> 41 42 using namespace ::com::sun::star; 43 using namespace ::com::sun::star::uno; 44 using namespace ::com::sun::star::lang; 45 using namespace ::com::sun::star::beans; 46 using namespace ::com::sun::star::accessibility; 47 48 VCLXAccessibleBox::VCLXAccessibleBox (VCLXWindow* pVCLWindow, BoxType aType, bool bIsDropDownBox) 49 : VCLXAccessibleComponent (pVCLWindow), 50 m_aBoxType (aType), 51 m_bIsDropDownBox (bIsDropDownBox), 52 m_nIndexInParent (DEFAULT_INDEX_IN_PARENT) 53 { 54 // Set up the flags that indicate which children this object has. 55 m_bHasListChild = true; 56 57 // A text field is not present for non drop down list boxes. 58 if ((m_aBoxType==LISTBOX) && ! m_bIsDropDownBox) 59 m_bHasTextChild = false; 60 else 61 m_bHasTextChild = true; 62 } 63 64 VCLXAccessibleBox::~VCLXAccessibleBox (void) 65 { 66 } 67 68 void VCLXAccessibleBox::ProcessWindowChildEvent( const VclWindowEvent& rVclWindowEvent ) 69 { 70 uno::Any aOldValue, aNewValue; 71 uno::Reference<XAccessible> xAcc; 72 73 switch ( rVclWindowEvent.GetId() ) 74 { 75 case VCLEVENT_WINDOW_SHOW: 76 case VCLEVENT_WINDOW_HIDE: 77 { 78 Window* pChildWindow = (Window *) rVclWindowEvent.GetData(); 79 // Just compare to the combo box text field. All other children 80 // are identical to this object in which case this object will 81 // be removed in a short time. 82 if (m_aBoxType==COMBOBOX) 83 { 84 ComboBox* pComboBox = static_cast<ComboBox*>(GetWindow()); 85 if ( ( pComboBox != NULL ) && ( pChildWindow != NULL ) ) 86 if (pChildWindow == pComboBox->GetSubEdit()) 87 { 88 if (rVclWindowEvent.GetId() == VCLEVENT_WINDOW_SHOW) 89 { 90 // Instantiate text field. 91 getAccessibleChild (0); 92 aNewValue <<= m_xText; 93 } 94 else 95 { 96 // Release text field. 97 aOldValue <<= m_xText; 98 m_xText = NULL; 99 } 100 // Tell the listeners about the new/removed child. 101 NotifyAccessibleEvent ( 102 AccessibleEventId::CHILD, 103 aOldValue, aNewValue); 104 } 105 106 } 107 } 108 break; 109 110 default: 111 VCLXAccessibleComponent::ProcessWindowChildEvent (rVclWindowEvent); 112 } 113 } 114 115 void VCLXAccessibleBox::ProcessWindowEvent (const VclWindowEvent& rVclWindowEvent) 116 { 117 switch ( rVclWindowEvent.GetId() ) 118 { 119 case VCLEVENT_DROPDOWN_SELECT: 120 case VCLEVENT_LISTBOX_SELECT: 121 case VCLEVENT_LISTBOX_FOCUSITEMCHANGED: 122 123 { 124 // Forward the call to the list child. 125 VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get()); 126 if ( pList == NULL ) 127 { 128 getAccessibleChild ( m_bHasTextChild ? 1 : 0 ); 129 pList = static_cast<VCLXAccessibleList*>(m_xList.get()); 130 } 131 if ( pList != NULL ) 132 { 133 pList->ProcessWindowEvent (rVclWindowEvent, m_bIsDropDownBox); 134 if(m_bIsDropDownBox) 135 { 136 NotifyAccessibleEvent(AccessibleEventId::VALUE_CHANGED, Any(), Any()); 137 Any aOldValue; 138 Any aNewValue; 139 aOldValue <<= AccessibleStateType::INDETERMINATE; 140 NotifyAccessibleEvent(AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue); 141 142 } 143 } 144 break; 145 } 146 case VCLEVENT_DROPDOWN_OPEN: 147 { 148 VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get()); 149 if ( pList == NULL ) 150 { 151 getAccessibleChild ( m_bHasTextChild ? 1 : 0 ); 152 pList = static_cast<VCLXAccessibleList*>(m_xList.get()); 153 } 154 if ( pList != NULL ) 155 { 156 pList->ProcessWindowEvent (rVclWindowEvent); 157 pList->HandleDropOpen(); 158 } 159 break; 160 } 161 case VCLEVENT_DROPDOWN_CLOSE: 162 { 163 VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get()); 164 if ( pList == NULL ) 165 { 166 getAccessibleChild ( m_bHasTextChild ? 1 : 0 ); 167 pList = static_cast<VCLXAccessibleList*>(m_xList.get()); 168 } 169 if ( pList != NULL ) 170 { 171 pList->ProcessWindowEvent (rVclWindowEvent); 172 } 173 Window* pWindow = GetWindow(); 174 if( pWindow && (pWindow->HasFocus() || pWindow->HasChildPathFocus()) ) 175 { 176 Any aOldValue, aNewValue; 177 aNewValue <<= AccessibleStateType::FOCUSED; 178 NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); 179 } 180 break; 181 } 182 case VCLEVENT_COMBOBOX_SELECT: 183 { 184 VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get()); 185 if (pList != NULL && m_xText.is()) 186 { 187 Reference<XAccessibleText> xText (m_xText->getAccessibleContext(), UNO_QUERY); 188 if ( xText.is() ) 189 { 190 ::rtl::OUString sText = xText->getSelectedText(); 191 if ( sText.isEmpty() ) 192 sText = xText->getText(); 193 pList->UpdateSelection_Acc (sText, m_bIsDropDownBox); 194 //if(m_bIsDropDownBox && !pList->IsInDropDown()) 195 if (m_bIsDropDownBox || ( !m_bIsDropDownBox && m_aBoxType==COMBOBOX)) 196 NotifyAccessibleEvent(AccessibleEventId::VALUE_CHANGED, Any(), Any()); 197 198 Any aOldValue; 199 Any aNewValue; 200 aOldValue <<= AccessibleStateType::INDETERMINATE; 201 NotifyAccessibleEvent(AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue); 202 203 } 204 } 205 break; 206 } 207 //case VCLEVENT_DROPDOWN_OPEN: 208 //case VCLEVENT_DROPDOWN_CLOSE: 209 case VCLEVENT_LISTBOX_DOUBLECLICK: 210 case VCLEVENT_LISTBOX_SCROLLED: 211 //case VCLEVENT_LISTBOX_SELECT: 212 case VCLEVENT_LISTBOX_ITEMADDED: 213 case VCLEVENT_LISTBOX_ITEMREMOVED: 214 case VCLEVENT_COMBOBOX_ITEMADDED: 215 case VCLEVENT_COMBOBOX_ITEMREMOVED: 216 case VCLEVENT_COMBOBOX_SCROLLED: 217 { 218 // Forward the call to the list child. 219 VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get()); 220 if ( pList == NULL ) 221 { 222 getAccessibleChild ( m_bHasTextChild ? 1 : 0 ); 223 pList = static_cast<VCLXAccessibleList*>(m_xList.get()); 224 } 225 if ( pList != NULL ) 226 pList->ProcessWindowEvent (rVclWindowEvent); 227 break; 228 } 229 230 //case VCLEVENT_COMBOBOX_SELECT: 231 case VCLEVENT_COMBOBOX_DESELECT: 232 { 233 // Selection is handled by VCLXAccessibleList which operates on 234 // the same VCL object as this box does. In case of the 235 // combobox, however, we have to help the list with providing 236 // the text of the currently selected item. 237 VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get()); 238 if (pList != NULL && m_xText.is()) 239 { 240 Reference<XAccessibleText> xText (m_xText->getAccessibleContext(), UNO_QUERY); 241 if ( xText.is() ) 242 { 243 ::rtl::OUString sText = xText->getSelectedText(); 244 if ( sText.isEmpty() ) 245 sText = xText->getText(); 246 pList->UpdateSelection (sText); 247 } 248 } 249 break; 250 } 251 252 case VCLEVENT_EDIT_MODIFY: 253 case VCLEVENT_EDIT_SELECTIONCHANGED: 254 // case VCLEVENT_EDIT_CARETCHANGED: 255 // Modify/Selection events are handled by the combo box instead of 256 // directly by the edit field (Why?). Therefore, delegate this 257 // call to the edit field. 258 if (m_aBoxType==COMBOBOX) 259 { 260 if (m_xText.is()) 261 { 262 Reference<XAccessibleContext> xContext = m_xText->getAccessibleContext(); 263 VCLXAccessibleEdit* pEdit = static_cast<VCLXAccessibleEdit*>(xContext.get()); 264 if (pEdit != NULL) 265 pEdit->ProcessWindowEvent (rVclWindowEvent); 266 } 267 } 268 break; 269 /* 270 // MT: Not sending VCLEVENT_LISTBOX_STATEUPDATE, see comment in ListBox::SelectEntryPos 271 case VCLEVENT_LISTBOX_STATEUPDATE: 272 { 273 // Need to update the INDETERMINATE state sometimes 274 if (m_bIsDropDownBox && m_aBoxType==LISTBOX) 275 { 276 sal_Int32 nSelectedEntryCount = 0; 277 ListBox* pListBox = static_cast< ListBox* >( GetWindow() ); 278 if (pListBox != NULL && pListBox->GetEntryCount() > 0) 279 { 280 nSelectedEntryCount = pListBox->GetSelectEntryCount(); 281 Any aOldValue; 282 Any aNewValue; 283 if ( nSelectedEntryCount == 0) 284 aNewValue <<= AccessibleStateType::INDETERMINATE; 285 else 286 aOldValue <<= AccessibleStateType::INDETERMINATE; 287 NotifyAccessibleEvent(AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue); 288 } 289 } 290 break; 291 } 292 */ 293 default: 294 VCLXAccessibleComponent::ProcessWindowEvent( rVclWindowEvent ); 295 } 296 } 297 298 IMPLEMENT_FORWARD_XINTERFACE2(VCLXAccessibleBox, VCLXAccessibleComponent, VCLXAccessibleBox_BASE) 299 IMPLEMENT_FORWARD_XTYPEPROVIDER2(VCLXAccessibleBox, VCLXAccessibleComponent, VCLXAccessibleBox_BASE) 300 301 //===== XAccessible ========================================================= 302 303 Reference< XAccessibleContext > SAL_CALL VCLXAccessibleBox::getAccessibleContext( ) 304 throw (RuntimeException) 305 { 306 ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); 307 308 return this; 309 } 310 311 //===== XAccessibleContext ================================================== 312 313 sal_Int32 SAL_CALL VCLXAccessibleBox::getAccessibleChildCount (void) 314 throw (RuntimeException) 315 { 316 vos::OGuard aSolarGuard( Application::GetSolarMutex() ); 317 ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); 318 319 // Usually a box has a text field and a list of items as its children. 320 // Non drop down list boxes have no text field. Additionally check 321 // whether the object is valid. 322 sal_Int32 nCount = 0; 323 if (IsValid()) 324 nCount += (m_bHasTextChild?1:0) + (m_bHasListChild?1:0); 325 else 326 { 327 // Object not valid anymore. Release references to children. 328 m_bHasTextChild = false; 329 m_xText = NULL; 330 m_bHasListChild = false; 331 m_xList = NULL; 332 } 333 334 return nCount; 335 } 336 337 Reference<XAccessible> SAL_CALL VCLXAccessibleBox::getAccessibleChild (sal_Int32 i) 338 throw (IndexOutOfBoundsException, RuntimeException) 339 { 340 vos::OGuard aSolarGuard( Application::GetSolarMutex() ); 341 ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); 342 343 if (i<0 || i>=getAccessibleChildCount()) 344 throw IndexOutOfBoundsException(); 345 346 Reference< XAccessible > xChild; 347 if (IsValid()) 348 { 349 if (i==1 || ! m_bHasTextChild) 350 { 351 // List. 352 if ( ! m_xList.is()) 353 { 354 VCLXAccessibleList* pList = new VCLXAccessibleList ( GetVCLXWindow(), 355 (m_aBoxType == LISTBOX ? VCLXAccessibleList::LISTBOX : VCLXAccessibleList::COMBOBOX), 356 this); 357 pList->SetIndexInParent (i); 358 m_xList = pList; 359 } 360 xChild = m_xList; 361 } 362 else 363 { 364 // Text Field. 365 if ( ! m_xText.is()) 366 { 367 if (m_aBoxType==COMBOBOX) 368 { 369 ComboBox* pComboBox = static_cast<ComboBox*>(GetWindow()); 370 if (pComboBox!=NULL && pComboBox->GetSubEdit()!=NULL) 371 //Set the edit's acc name the same as parent 372 { 373 pComboBox->GetSubEdit()->SetAccessibleName(getAccessibleName()); 374 m_xText = pComboBox->GetSubEdit()->GetAccessible(); 375 } 376 } 377 else if (m_bIsDropDownBox) 378 m_xText = new VCLXAccessibleTextField (GetVCLXWindow(),this); 379 } 380 xChild = m_xText; 381 } 382 } 383 384 return xChild; 385 } 386 387 sal_Int16 SAL_CALL VCLXAccessibleBox::getAccessibleRole (void) throw (RuntimeException) 388 { 389 ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); 390 391 // Return the role <const>COMBO_BOX</const> for both VCL combo boxes and 392 // VCL list boxes in DropDown-Mode else <const>PANEL</const>. 393 // This way the Java bridge has not to handle both independently. 394 //return m_bIsDropDownBox ? AccessibleRole::COMBO_BOX : AccessibleRole::PANEL; 395 if (m_bIsDropDownBox || (!m_bIsDropDownBox && m_aBoxType == COMBOBOX )) 396 return AccessibleRole::COMBO_BOX; 397 else 398 return AccessibleRole::PANEL; 399 } 400 401 sal_Int32 SAL_CALL VCLXAccessibleBox::getAccessibleIndexInParent (void) 402 throw (::com::sun::star::uno::RuntimeException) 403 { 404 if (m_nIndexInParent != DEFAULT_INDEX_IN_PARENT) 405 return m_nIndexInParent; 406 else 407 return VCLXAccessibleComponent::getAccessibleIndexInParent(); 408 } 409 410 //===== XAccessibleAction =================================================== 411 412 sal_Int32 SAL_CALL VCLXAccessibleBox::getAccessibleActionCount (void) 413 throw (RuntimeException) 414 { 415 ::osl::Guard< ::osl::Mutex> aGuard (GetMutex()); 416 417 // There is one action for drop down boxes (toggle popup) and none for 418 // the other boxes. 419 return m_bIsDropDownBox ? 1 : 0; 420 } 421 422 sal_Bool SAL_CALL VCLXAccessibleBox::doAccessibleAction (sal_Int32 nIndex) 423 throw (IndexOutOfBoundsException, RuntimeException) 424 { 425 sal_Bool bNotify = sal_False; 426 427 { 428 vos::OGuard aSolarGuard( Application::GetSolarMutex() ); 429 ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); 430 431 if (nIndex<0 || nIndex>=getAccessibleActionCount()) 432 throw ::com::sun::star::lang::IndexOutOfBoundsException(); 433 434 if (m_aBoxType == COMBOBOX) 435 { 436 ComboBox* pComboBox = static_cast< ComboBox* >( GetWindow() ); 437 if (pComboBox != NULL) 438 { 439 pComboBox->ToggleDropDown(); 440 bNotify = sal_True; 441 } 442 } 443 else if (m_aBoxType == LISTBOX) 444 { 445 ListBox* pListBox = static_cast< ListBox* >( GetWindow() ); 446 if (pListBox != NULL) 447 { 448 pListBox->ToggleDropDown(); 449 bNotify = sal_True; 450 } 451 } 452 } 453 454 if (bNotify) 455 NotifyAccessibleEvent (AccessibleEventId::ACTION_CHANGED, Any(), Any()); 456 457 return bNotify; 458 } 459 460 ::rtl::OUString SAL_CALL VCLXAccessibleBox::getAccessibleActionDescription (sal_Int32 nIndex) 461 throw (IndexOutOfBoundsException, RuntimeException) 462 { 463 ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); 464 if (nIndex<0 || nIndex>=getAccessibleActionCount()) 465 throw ::com::sun::star::lang::IndexOutOfBoundsException(); 466 467 return m_bIsDropDownBox ? TK_RES_STRING( RID_STR_ACC_ACTION_TOGGLEPOPUP) : ::rtl::OUString(); 468 } 469 470 Reference< XAccessibleKeyBinding > VCLXAccessibleBox::getAccessibleActionKeyBinding( sal_Int32 nIndex ) 471 throw (IndexOutOfBoundsException, RuntimeException) 472 { 473 ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); 474 475 Reference< XAccessibleKeyBinding > xRet; 476 477 if (nIndex<0 || nIndex>=getAccessibleActionCount()) 478 throw ::com::sun::star::lang::IndexOutOfBoundsException(); 479 480 // ... which key? 481 return xRet; 482 } 483 484 //===== XComponent ========================================================== 485 486 void SAL_CALL VCLXAccessibleBox::disposing (void) 487 { 488 VCLXAccessibleComponent::disposing(); 489 } 490 491 // ===== XAccessibleValue =============================================== 492 Any VCLXAccessibleBox::getCurrentValue( ) 493 throw( RuntimeException ) 494 { 495 vos::OGuard aSolarGuard( Application::GetSolarMutex() ); 496 ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); 497 498 Any aAny; 499 if( m_xList.is() && m_xText.is()) 500 { 501 // VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get()); 502 Reference<XAccessibleText> xText (m_xText->getAccessibleContext(), UNO_QUERY); 503 if ( xText.is() ) 504 { 505 ::rtl::OUString sText = xText->getText(); 506 aAny <<= sText; 507 } 508 } 509 if (m_aBoxType == LISTBOX && m_bIsDropDownBox && m_xList.is() ) 510 { 511 512 VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get()); 513 if(pList->IsInDropDown()) 514 { 515 if(pList->getSelectedAccessibleChildCount()>0) 516 { 517 Reference<XAccessibleContext> xName (pList->getSelectedAccessibleChild((sal_Int32)(0)), UNO_QUERY); 518 if(xName.is()) 519 { 520 aAny <<= xName->getAccessibleName(); 521 } 522 } 523 } 524 } 525 526 return aAny; 527 } 528 529 sal_Bool VCLXAccessibleBox::setCurrentValue( const Any& aNumber ) 530 throw( RuntimeException ) 531 { 532 vos::OGuard aSolarGuard( Application::GetSolarMutex() ); 533 ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); 534 535 ::rtl::OUString fValue; 536 sal_Bool bValid = (aNumber >>= fValue); 537 if( bValid ) 538 { 539 540 } 541 return bValid; 542 543 } 544 545 Any VCLXAccessibleBox::getMaximumValue( ) 546 throw( RuntimeException ) 547 { 548 Any aAny; 549 return aAny; 550 } 551 552 Any VCLXAccessibleBox::getMinimumValue( ) 553 throw( RuntimeException ) 554 { 555 Any aAny; 556 return aAny; 557 } 558 559 // Set the INDETERMINATE state when there is no selected item for combobox 560 void VCLXAccessibleBox::FillAccessibleStateSet( utl::AccessibleStateSetHelper& rStateSet ) 561 { 562 VCLXAccessibleComponent::FillAccessibleStateSet(rStateSet); 563 if (m_aBoxType == COMBOBOX ) 564 { 565 ::rtl::OUString sText; 566 sal_Int32 nEntryCount = 0; 567 ComboBox* pComboBox = static_cast<ComboBox*>(GetWindow()); 568 if (pComboBox != NULL) 569 { 570 Edit* pSubEdit = pComboBox->GetSubEdit(); 571 if ( pSubEdit) 572 sText = pSubEdit->GetText(); 573 nEntryCount = pComboBox->GetEntryCount(); 574 } 575 if ( sText.isEmpty() && nEntryCount > 0 ) 576 rStateSet.AddState(AccessibleStateType::INDETERMINATE); 577 } 578 else if (m_aBoxType == LISTBOX && m_bIsDropDownBox == true) 579 { 580 sal_Int32 nSelectedEntryCount = 0; 581 ListBox* pListBox = static_cast< ListBox* >( GetWindow() ); 582 if (pListBox != NULL && pListBox->GetEntryCount() > 0) 583 { 584 nSelectedEntryCount = pListBox->GetSelectEntryCount(); 585 if ( nSelectedEntryCount == 0) 586 rStateSet.AddState(AccessibleStateType::INDETERMINATE); 587 } 588 } 589 } 590