/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::com::sun::star; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::lang; using namespace ::com::sun::star::beans; using namespace ::com::sun::star::accessibility; VCLXAccessibleBox::VCLXAccessibleBox (VCLXWindow* pVCLWindow, BoxType aType, bool bIsDropDownBox) : VCLXAccessibleComponent (pVCLWindow), m_aBoxType (aType), m_bIsDropDownBox (bIsDropDownBox) { // Set up the flags that indicate which children this object has. m_bHasListChild = true; // A text field is not present for non drop down list boxes. if ((m_aBoxType==LISTBOX) && ! m_bIsDropDownBox) m_bHasTextChild = false; else m_bHasTextChild = true; } void VCLXAccessibleBox::ProcessWindowChildEvent( const VclWindowEvent& rVclWindowEvent ) { uno::Any aOldValue, aNewValue; switch ( rVclWindowEvent.GetId() ) { case VclEventId::WindowShow: case VclEventId::WindowHide: { vcl::Window* pChildWindow = static_cast(rVclWindowEvent.GetData()); // Just compare to the combo box text field. All other children // are identical to this object in which case this object will // be removed in a short time. if (m_aBoxType==COMBOBOX) { VclPtr< ComboBox > pComboBox = GetAs< ComboBox >(); if ( ( pComboBox != nullptr ) && ( pChildWindow != nullptr ) ) if (pChildWindow == pComboBox->GetSubEdit()) { if (rVclWindowEvent.GetId() == VclEventId::WindowShow) { // Instantiate text field. getAccessibleChild (0); aNewValue <<= m_xText; } else { // Release text field. aOldValue <<= m_xText; m_xText = nullptr; } // Tell the listeners about the new/removed child. NotifyAccessibleEvent ( AccessibleEventId::CHILD, aOldValue, aNewValue); } } } break; default: VCLXAccessibleComponent::ProcessWindowChildEvent (rVclWindowEvent); } } void VCLXAccessibleBox::ProcessWindowEvent (const VclWindowEvent& rVclWindowEvent) { switch ( rVclWindowEvent.GetId() ) { case VclEventId::DropdownSelect: case VclEventId::ListboxSelect: { // Forward the call to the list child. VCLXAccessibleList* pList = static_cast(m_xList.get()); if ( pList == nullptr ) { getAccessibleChild ( m_bHasTextChild ? 1 : 0 ); pList = static_cast(m_xList.get()); } if ( pList != nullptr ) { pList->ProcessWindowEvent (rVclWindowEvent, m_bIsDropDownBox); #if defined(_WIN32) if (m_bIsDropDownBox) { NotifyAccessibleEvent(AccessibleEventId::VALUE_CHANGED, Any(), Any()); } #endif } break; } case VclEventId::DropdownOpen: { VCLXAccessibleList* pList = static_cast(m_xList.get()); if ( pList == nullptr ) { getAccessibleChild ( m_bHasTextChild ? 1 : 0 ); pList = static_cast(m_xList.get()); } if ( pList != nullptr ) { pList->ProcessWindowEvent (rVclWindowEvent); pList->HandleDropOpen(); } break; } case VclEventId::DropdownClose: { VCLXAccessibleList* pList = static_cast(m_xList.get()); if ( pList == nullptr ) { getAccessibleChild ( m_bHasTextChild ? 1 : 0 ); pList = static_cast(m_xList.get()); } if ( pList != nullptr ) { pList->ProcessWindowEvent (rVclWindowEvent); } VclPtr pWindow = GetWindow(); if( pWindow && (pWindow->HasFocus() || pWindow->HasChildPathFocus()) ) { Any aOldValue, aNewValue; aNewValue <<= AccessibleStateType::FOCUSED; NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); } break; } case VclEventId::ComboboxSelect: { VCLXAccessibleList* pList = static_cast(m_xList.get()); if (pList != nullptr && m_xText.is()) { Reference xText (m_xText->getAccessibleContext(), UNO_QUERY); if ( xText.is() ) { OUString sText = xText->getSelectedText(); if ( sText.isEmpty() ) sText = xText->getText(); pList->UpdateSelection_Acc(sText, m_bIsDropDownBox); #if defined(_WIN32) if (m_bIsDropDownBox || m_aBoxType==COMBOBOX) NotifyAccessibleEvent(AccessibleEventId::VALUE_CHANGED, Any(), Any()); #endif } } break; } //case VclEventId::DropdownOpen: //case VclEventId::DropdownClose: case VclEventId::ListboxDoubleClick: case VclEventId::ListboxScrolled: //case VclEventId::ListboxSelect: case VclEventId::ListboxItemAdded: case VclEventId::ListboxItemRemoved: case VclEventId::ComboboxItemAdded: case VclEventId::ComboboxItemRemoved: { // Forward the call to the list child. VCLXAccessibleList* pList = static_cast(m_xList.get()); if ( pList == nullptr ) { getAccessibleChild ( m_bHasTextChild ? 1 : 0 ); pList = static_cast(m_xList.get()); } if ( pList != nullptr ) pList->ProcessWindowEvent (rVclWindowEvent); break; } //case VclEventId::ComboboxSelect: case VclEventId::ComboboxDeselect: { // Selection is handled by VCLXAccessibleList which operates on // the same VCL object as this box does. In case of the // combobox, however, we have to help by providing the list with // the text of the currently selected item. VCLXAccessibleList* pList = static_cast(m_xList.get()); if (pList != nullptr && m_xText.is()) { Reference xText (m_xText->getAccessibleContext(), UNO_QUERY); if ( xText.is() ) { OUString sText = xText->getSelectedText(); if ( sText.isEmpty() ) sText = xText->getText(); pList->UpdateSelection (sText); } } break; } case VclEventId::EditModify: case VclEventId::EditSelectionChanged: case VclEventId::EditCaretChanged: // Modify/Selection events are handled by the combo box instead of // directly by the edit field (Why?). Therefore, delegate this // call to the edit field. if (m_aBoxType==COMBOBOX) { if (m_xText.is()) { Reference xContext = m_xText->getAccessibleContext(); VCLXAccessibleEdit* pEdit = static_cast(xContext.get()); if (pEdit != nullptr) pEdit->ProcessWindowEvent (rVclWindowEvent); } } break; default: VCLXAccessibleComponent::ProcessWindowEvent( rVclWindowEvent ); } } IMPLEMENT_FORWARD_XINTERFACE2(VCLXAccessibleBox, VCLXAccessibleComponent, VCLXAccessibleBox_BASE) IMPLEMENT_FORWARD_XTYPEPROVIDER2(VCLXAccessibleBox, VCLXAccessibleComponent, VCLXAccessibleBox_BASE) //===== XAccessible ========================================================= Reference< XAccessibleContext > SAL_CALL VCLXAccessibleBox::getAccessibleContext( ) { ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); return this; } //===== XAccessibleContext ================================================== sal_Int32 VCLXAccessibleBox::getAccessibleChildCount() { SolarMutexGuard aSolarGuard; ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); return implGetAccessibleChildCount(); } sal_Int32 VCLXAccessibleBox::implGetAccessibleChildCount() { // Usually a box has a text field and a list of items as its children. // Non drop down list boxes have no text field. Additionally check // whether the object is valid. sal_Int32 nCount = 0; if (IsValid()) nCount += (m_bHasTextChild?1:0) + (m_bHasListChild?1:0); else { // Object not valid anymore. Release references to children. m_bHasTextChild = false; m_xText = nullptr; m_bHasListChild = false; m_xList = nullptr; } return nCount; } Reference SAL_CALL VCLXAccessibleBox::getAccessibleChild (sal_Int32 i) { SolarMutexGuard aSolarGuard; ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); if (i<0 || i>=implGetAccessibleChildCount()) throw IndexOutOfBoundsException(); Reference< XAccessible > xChild; if (IsValid()) { if (i==1 || ! m_bHasTextChild) { // List. if ( ! m_xList.is()) { rtl::Reference pList = new VCLXAccessibleList ( GetVCLXWindow(), (m_aBoxType == LISTBOX ? VCLXAccessibleList::LISTBOX : VCLXAccessibleList::COMBOBOX), this); pList->SetIndexInParent (i); m_xList = pList; } xChild = m_xList; } else { // Text Field. if ( ! m_xText.is()) { if (m_aBoxType==COMBOBOX) { VclPtr< ComboBox > pComboBox = GetAs< ComboBox >(); if (pComboBox!=nullptr && pComboBox->GetSubEdit()!=nullptr) //Set the edit's acc name the same as parent { pComboBox->GetSubEdit()->SetAccessibleName(getAccessibleName()); m_xText = pComboBox->GetSubEdit()->GetAccessible(); } } else if (m_bIsDropDownBox) m_xText = new VCLXAccessibleTextField (GetVCLXWindow(),this); } xChild = m_xText; } } return xChild; } sal_Int16 SAL_CALL VCLXAccessibleBox::getAccessibleRole() { ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); // Return the role COMBO_BOX for both VCL combo boxes and // VCL list boxes in DropDown-Mode else PANEL. // This way the Java bridge has not to handle both independently. //return m_bIsDropDownBox ? AccessibleRole::COMBO_BOX : AccessibleRole::PANEL; if (m_bIsDropDownBox || (m_aBoxType == COMBOBOX)) return AccessibleRole::COMBO_BOX; else return AccessibleRole::PANEL; } //===== XAccessibleAction =================================================== sal_Int32 SAL_CALL VCLXAccessibleBox::getAccessibleActionCount() { ::osl::Guard< ::osl::Mutex> aGuard (GetMutex()); // There is one action for drop down boxes (toggle popup) and none for // the other boxes. return m_bIsDropDownBox ? 1 : 0; } sal_Bool SAL_CALL VCLXAccessibleBox::doAccessibleAction (sal_Int32 nIndex) { bool bNotify = false; { SolarMutexGuard aSolarGuard; ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); if (nIndex!=0 || !m_bIsDropDownBox) throw css::lang::IndexOutOfBoundsException( ("VCLXAccessibleBox::doAccessibleAction: index " + OUString::number(nIndex) + " not among 0.." + OUString::number(getAccessibleActionCount())), static_cast(this)); if (m_aBoxType == COMBOBOX) { VclPtr< ComboBox > pComboBox = GetAs< ComboBox >(); if (pComboBox != nullptr) { pComboBox->ToggleDropDown(); bNotify = true; } } else if (m_aBoxType == LISTBOX) { VclPtr< ListBox > pListBox = GetAs< ListBox >(); if (pListBox != nullptr) { pListBox->ToggleDropDown(); bNotify = true; } } } if (bNotify) NotifyAccessibleEvent (AccessibleEventId::ACTION_CHANGED, Any(), Any()); return bNotify; } OUString SAL_CALL VCLXAccessibleBox::getAccessibleActionDescription (sal_Int32 nIndex) { ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); if (nIndex!=0 || !m_bIsDropDownBox) throw css::lang::IndexOutOfBoundsException(); return RID_STR_ACC_ACTION_TOGGLEPOPUP; } Reference< XAccessibleKeyBinding > VCLXAccessibleBox::getAccessibleActionKeyBinding( sal_Int32 nIndex ) { ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); Reference< XAccessibleKeyBinding > xRet; if (nIndex<0 || nIndex>=getAccessibleActionCount()) throw css::lang::IndexOutOfBoundsException(); // ... which key? return xRet; } // ===== XAccessibleValue =============================================== Any VCLXAccessibleBox::getCurrentValue( ) { SolarMutexGuard aSolarGuard; ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); Any aAny; if( m_xList.is() && m_xText.is()) { // VCLXAccessibleList* pList = static_cast(m_xList.get()); Reference xText (m_xText->getAccessibleContext(), UNO_QUERY); if ( xText.is() ) { OUString sText = xText->getText(); aAny <<= sText; } } if (m_aBoxType == LISTBOX && m_bIsDropDownBox && m_xList.is() ) { VCLXAccessibleList* pList = static_cast(m_xList.get()); if(pList->IsInDropDown()) { if(pList->getSelectedAccessibleChildCount()>0) { Reference xName (pList->getSelectedAccessibleChild(sal_Int32(0)), UNO_QUERY); if(xName.is()) { aAny <<= xName->getAccessibleName(); } } } } return aAny; } sal_Bool VCLXAccessibleBox::setCurrentValue( const Any& aNumber ) { SolarMutexGuard aSolarGuard; ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); OUString fValue; bool bValid = (aNumber >>= fValue); return bValid; } Any VCLXAccessibleBox::getMaximumValue( ) { Any aAny; return aAny; } Any VCLXAccessibleBox::getMinimumValue( ) { Any aAny; return aAny; } Any VCLXAccessibleBox::getMinimumIncrement( ) { return Any(); } // Set the INDETERMINATE state when there is no selected item for combobox void VCLXAccessibleBox::FillAccessibleStateSet( utl::AccessibleStateSetHelper& rStateSet ) { VCLXAccessibleComponent::FillAccessibleStateSet(rStateSet); if (m_aBoxType == COMBOBOX ) { OUString sText; sal_Int32 nEntryCount = 0; VclPtr< ComboBox > pComboBox = GetAs< ComboBox >(); if (pComboBox != nullptr) { Edit* pSubEdit = pComboBox->GetSubEdit(); if ( pSubEdit) sText = pSubEdit->GetText(); nEntryCount = pComboBox->GetEntryCount(); } if ( sText.isEmpty() && nEntryCount > 0 ) rStateSet.AddState(AccessibleStateType::INDETERMINATE); } else if (m_aBoxType == LISTBOX && m_bIsDropDownBox) { VclPtr< ListBox > pListBox = GetAs< ListBox >(); if (pListBox != nullptr && pListBox->GetEntryCount() > 0) { sal_Int32 nSelectedEntryCount = pListBox->GetSelectedEntryCount(); if ( nSelectedEntryCount == 0) rStateSet.AddState(AccessibleStateType::INDETERMINATE); } } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */