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 { 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 { 314 vos::OGuard aSolarGuard( Application::GetSolarMutex() ); 315 ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); 316 317 // Usually a box has a text field and a list of items as its children. 318 // Non drop down list boxes have no text field. Additionally check 319 // whether the object is valid. 320 sal_Int32 nCount = 0; 321 if (IsValid()) 322 nCount += (m_bHasTextChild?1:0) + (m_bHasListChild?1:0); 323 else 324 { 325 // Object not valid anymore. Release references to children. 326 m_bHasTextChild = false; 327 m_xText = NULL; 328 m_bHasListChild = false; 329 m_xList = NULL; 330 } 331 332 return nCount; 333 } 334 335 Reference<XAccessible> SAL_CALL VCLXAccessibleBox::getAccessibleChild (sal_Int32 i) 336 { 337 vos::OGuard aSolarGuard( Application::GetSolarMutex() ); 338 ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); 339 340 if (i<0 || i>=getAccessibleChildCount()) 341 throw IndexOutOfBoundsException(); 342 343 Reference< XAccessible > xChild; 344 if (IsValid()) 345 { 346 if (i==1 || ! m_bHasTextChild) 347 { 348 // List. 349 if ( ! m_xList.is()) 350 { 351 VCLXAccessibleList* pList = new VCLXAccessibleList ( GetVCLXWindow(), 352 (m_aBoxType == LISTBOX ? VCLXAccessibleList::LISTBOX : VCLXAccessibleList::COMBOBOX), 353 this); 354 pList->SetIndexInParent (i); 355 m_xList = pList; 356 } 357 xChild = m_xList; 358 } 359 else 360 { 361 // Text Field. 362 if ( ! m_xText.is()) 363 { 364 if (m_aBoxType==COMBOBOX) 365 { 366 ComboBox* pComboBox = static_cast<ComboBox*>(GetWindow()); 367 if (pComboBox!=NULL && pComboBox->GetSubEdit()!=NULL) 368 //Set the edit's acc name the same as parent 369 { 370 pComboBox->GetSubEdit()->SetAccessibleName(getAccessibleName()); 371 m_xText = pComboBox->GetSubEdit()->GetAccessible(); 372 } 373 } 374 else if (m_bIsDropDownBox) 375 m_xText = new VCLXAccessibleTextField (GetVCLXWindow(),this); 376 } 377 xChild = m_xText; 378 } 379 } 380 381 return xChild; 382 } 383 384 sal_Int16 SAL_CALL VCLXAccessibleBox::getAccessibleRole (void) 385 { 386 ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); 387 388 // Return the role <const>COMBO_BOX</const> for both VCL combo boxes and 389 // VCL list boxes in DropDown-Mode else <const>PANEL</const>. 390 // This way the Java bridge has not to handle both independently. 391 //return m_bIsDropDownBox ? AccessibleRole::COMBO_BOX : AccessibleRole::PANEL; 392 if (m_bIsDropDownBox || (!m_bIsDropDownBox && m_aBoxType == COMBOBOX )) 393 return AccessibleRole::COMBO_BOX; 394 else 395 return AccessibleRole::PANEL; 396 } 397 398 sal_Int32 SAL_CALL VCLXAccessibleBox::getAccessibleIndexInParent (void) 399 { 400 if (m_nIndexInParent != DEFAULT_INDEX_IN_PARENT) 401 return m_nIndexInParent; 402 else 403 return VCLXAccessibleComponent::getAccessibleIndexInParent(); 404 } 405 406 //===== XAccessibleAction =================================================== 407 408 sal_Int32 SAL_CALL VCLXAccessibleBox::getAccessibleActionCount (void) 409 { 410 ::osl::Guard< ::osl::Mutex> aGuard (GetMutex()); 411 412 // There is one action for drop down boxes (toggle popup) and none for 413 // the other boxes. 414 return m_bIsDropDownBox ? 1 : 0; 415 } 416 417 sal_Bool SAL_CALL VCLXAccessibleBox::doAccessibleAction (sal_Int32 nIndex) 418 { 419 sal_Bool bNotify = sal_False; 420 421 { 422 vos::OGuard aSolarGuard( Application::GetSolarMutex() ); 423 ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); 424 425 if (nIndex<0 || nIndex>=getAccessibleActionCount()) 426 throw ::com::sun::star::lang::IndexOutOfBoundsException(); 427 428 if (m_aBoxType == COMBOBOX) 429 { 430 ComboBox* pComboBox = static_cast< ComboBox* >( GetWindow() ); 431 if (pComboBox != NULL) 432 { 433 pComboBox->ToggleDropDown(); 434 bNotify = sal_True; 435 } 436 } 437 else if (m_aBoxType == LISTBOX) 438 { 439 ListBox* pListBox = static_cast< ListBox* >( GetWindow() ); 440 if (pListBox != NULL) 441 { 442 pListBox->ToggleDropDown(); 443 bNotify = sal_True; 444 } 445 } 446 } 447 448 if (bNotify) 449 NotifyAccessibleEvent (AccessibleEventId::ACTION_CHANGED, Any(), Any()); 450 451 return bNotify; 452 } 453 454 ::rtl::OUString SAL_CALL VCLXAccessibleBox::getAccessibleActionDescription (sal_Int32 nIndex) 455 { 456 ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); 457 if (nIndex<0 || nIndex>=getAccessibleActionCount()) 458 throw ::com::sun::star::lang::IndexOutOfBoundsException(); 459 460 return m_bIsDropDownBox ? TK_RES_STRING( RID_STR_ACC_ACTION_TOGGLEPOPUP) : ::rtl::OUString(); 461 } 462 463 Reference< XAccessibleKeyBinding > VCLXAccessibleBox::getAccessibleActionKeyBinding( sal_Int32 nIndex ) 464 { 465 ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); 466 467 Reference< XAccessibleKeyBinding > xRet; 468 469 if (nIndex<0 || nIndex>=getAccessibleActionCount()) 470 throw ::com::sun::star::lang::IndexOutOfBoundsException(); 471 472 // ... which key? 473 return xRet; 474 } 475 476 //===== XComponent ========================================================== 477 478 void SAL_CALL VCLXAccessibleBox::disposing (void) 479 { 480 VCLXAccessibleComponent::disposing(); 481 } 482 483 // ===== XAccessibleValue =============================================== 484 Any VCLXAccessibleBox::getCurrentValue( ) 485 { 486 vos::OGuard aSolarGuard( Application::GetSolarMutex() ); 487 ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); 488 489 Any aAny; 490 if( m_xList.is() && m_xText.is()) 491 { 492 // VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get()); 493 Reference<XAccessibleText> xText (m_xText->getAccessibleContext(), UNO_QUERY); 494 if ( xText.is() ) 495 { 496 ::rtl::OUString sText = xText->getText(); 497 aAny <<= sText; 498 } 499 } 500 if (m_aBoxType == LISTBOX && m_bIsDropDownBox && m_xList.is() ) 501 { 502 503 VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get()); 504 if(pList->IsInDropDown()) 505 { 506 if(pList->getSelectedAccessibleChildCount()>0) 507 { 508 Reference<XAccessibleContext> xName (pList->getSelectedAccessibleChild((sal_Int32)(0)), UNO_QUERY); 509 if(xName.is()) 510 { 511 aAny <<= xName->getAccessibleName(); 512 } 513 } 514 } 515 } 516 517 return aAny; 518 } 519 520 sal_Bool VCLXAccessibleBox::setCurrentValue( const Any& aNumber ) 521 { 522 vos::OGuard aSolarGuard( Application::GetSolarMutex() ); 523 ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); 524 525 ::rtl::OUString fValue; 526 sal_Bool bValid = (aNumber >>= fValue); 527 if( bValid ) 528 { 529 530 } 531 return bValid; 532 533 } 534 535 Any VCLXAccessibleBox::getMaximumValue( ) 536 { 537 Any aAny; 538 return aAny; 539 } 540 541 Any VCLXAccessibleBox::getMinimumValue( ) 542 { 543 Any aAny; 544 return aAny; 545 } 546 547 // Set the INDETERMINATE state when there is no selected item for combobox 548 void VCLXAccessibleBox::FillAccessibleStateSet( utl::AccessibleStateSetHelper& rStateSet ) 549 { 550 VCLXAccessibleComponent::FillAccessibleStateSet(rStateSet); 551 if (m_aBoxType == COMBOBOX ) 552 { 553 ::rtl::OUString sText; 554 sal_Int32 nEntryCount = 0; 555 ComboBox* pComboBox = static_cast<ComboBox*>(GetWindow()); 556 if (pComboBox != NULL) 557 { 558 Edit* pSubEdit = pComboBox->GetSubEdit(); 559 if ( pSubEdit) 560 sText = pSubEdit->GetText(); 561 nEntryCount = pComboBox->GetEntryCount(); 562 } 563 if ( sText.isEmpty() && nEntryCount > 0 ) 564 rStateSet.AddState(AccessibleStateType::INDETERMINATE); 565 } 566 else if (m_aBoxType == LISTBOX && m_bIsDropDownBox == true) 567 { 568 sal_Int32 nSelectedEntryCount = 0; 569 ListBox* pListBox = static_cast< ListBox* >( GetWindow() ); 570 if (pListBox != NULL && pListBox->GetEntryCount() > 0) 571 { 572 nSelectedEntryCount = pListBox->GetSelectEntryCount(); 573 if ( nSelectedEntryCount == 0) 574 rStateSet.AddState(AccessibleStateType::INDETERMINATE); 575 } 576 } 577 } 578