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