// This file is part of BOINC. // http://boinc.berkeley.edu // Copyright (C) 2009 University of California // // BOINC is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License // as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // // BOINC is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with BOINC. If not, see . // macAccessiblity.cpp #include #include "stdwx.h" #include "BOINCGUIApp.h" #include "BOINCBaseFrame.h" #include "BOINCBaseView.h" #include "DlgEventLog.h" #include "MainDocument.h" #include "AdvancedFrame.h" #include "BOINCListCtrl.h" #include "DlgEventLogListCtrl.h" #include "ProjectListCtrl.h" #include "ViewStatistics.h" #include "wxPieCtrl.h" #include "sg_BoincSimpleGUI.h" #include "Events.h" #include "macAccessiblity.h" #define MAX_LIST_COL 100 void AccessibilityIgnoreAllChildren(HIViewRef parent, int recursionLevel) { HIViewRef child; OSStatus err; if (recursionLevel > 100) { fprintf(stderr, "Error: AccessibilityIgnoreAllChildren recursion level > 100\n"); return; } child = HIViewGetFirstSubview(parent); while (child) { err = HIObjectSetAccessibilityIgnored((HIObjectRef)child, true); AccessibilityIgnoreAllChildren(child, recursionLevel + 1); child = HIViewGetNextView(child); } } OSStatus BOINCListAccessibilityEventHandler( EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void* pData, Boolean isHeader); pascal OSStatus BOINCListHeaderAccessibilityEventHandler( EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void* pData) { return BOINCListAccessibilityEventHandler(inHandlerCallRef, inEvent, pData, true); } pascal OSStatus BOINCListBodyAccessibilityEventHandler( EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void* pData) { return BOINCListAccessibilityEventHandler(inHandlerCallRef, inEvent, pData, false); } pascal OSStatus AttachListAccessibilityEventHandler( EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void* pData); static EventTypeSpec myAccessibilityEvents[] = { { kEventClassAccessibility, kEventAccessibleGetChildAtPoint }, { kEventClassAccessibility, kEventAccessibleGetFocusedChild }, { kEventClassAccessibility, kEventAccessibleGetAllAttributeNames }, { kEventClassAccessibility, kEventAccessibleGetAllParameterizedAttributeNames }, { kEventClassAccessibility, kEventAccessibleIsNamedAttributeSettable }, { kEventClassAccessibility, kEventAccessibleSetNamedAttribute }, { kEventClassAccessibility, kEventAccessibleGetNamedAttribute }, { kEventClassAccessibility, kEventAccessibleGetAllActionNames }, { kEventClassAccessibility, kEventAccessibleGetNamedActionDescription }, { kEventClassAccessibility, kEventAccessiblePerformNamedAction } }; static EventTypeSpec Simple_AccessibilityEvents[] = { { kEventClassAccessibility, kEventAccessibleGetNamedAttribute } }; pascal OSStatus SimpleAccessibilityEventHandler( EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void* pData); pascal OSStatus PieCtrlAccessibilityEventHandler( EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void* pData); void CBOINCListCtrl::SetupMacAccessibilitySupport() { #if !USE_NATIVE_LISTCONTROL HIViewRef listControlView; HIViewRef headerView; HIViewRef bodyView; OSErr err; listControlView = (HIViewRef)GetHandle(); headerView = HIViewGetFirstSubview(listControlView); bodyView = HIViewGetNextView(headerView); err = HIViewSetEnabled(headerView, true); accessibilityHandlerData.pList = (wxGenericListCtrl*)this; accessibilityHandlerData.pView = m_pParentView; accessibilityHandlerData.headerView = headerView; accessibilityHandlerData.bodyView = bodyView; accessibilityHandlerData.pEventLog = NULL; err = InstallHIObjectEventHandler((HIObjectRef)headerView, NewEventHandlerUPP(BOINCListHeaderAccessibilityEventHandler), sizeof(myAccessibilityEvents) / sizeof(EventTypeSpec), myAccessibilityEvents, &accessibilityHandlerData, &m_pHeaderAccessibilityEventHandlerRef); err = InstallHIObjectEventHandler((HIObjectRef)bodyView, NewEventHandlerUPP(BOINCListBodyAccessibilityEventHandler), sizeof(myAccessibilityEvents) / sizeof(EventTypeSpec), myAccessibilityEvents, &accessibilityHandlerData, &m_pBodyAccessibilityEventHandlerRef); #endif } void CBOINCListCtrl::RemoveMacAccessibilitySupport() { #if !USE_NATIVE_LISTCONTROL ::RemoveEventHandler(m_pHeaderAccessibilityEventHandlerRef); ::RemoveEventHandler(m_pBodyAccessibilityEventHandlerRef); #endif } void CDlgEventLogListCtrl::SetupMacAccessibilitySupport() { #if !USE_NATIVE_LISTCONTROL HIViewRef listControlView; HIViewRef headerView; HIViewRef bodyView; OSErr err; listControlView = (HIViewRef)GetHandle(); headerView = HIViewGetFirstSubview(listControlView); bodyView = HIViewGetNextView(headerView); err = HIViewSetEnabled(headerView, true); accessibilityHandlerData.pList = (wxGenericListCtrl*)this; accessibilityHandlerData.pView = NULL; accessibilityHandlerData.headerView = headerView; accessibilityHandlerData.bodyView = bodyView; accessibilityHandlerData.pEventLog = m_pParentView; err = InstallHIObjectEventHandler((HIObjectRef)headerView, NewEventHandlerUPP(BOINCListHeaderAccessibilityEventHandler), sizeof(myAccessibilityEvents) / sizeof(EventTypeSpec), myAccessibilityEvents, &accessibilityHandlerData, &m_pHeaderAccessibilityEventHandlerRef); err = InstallHIObjectEventHandler((HIObjectRef)bodyView, NewEventHandlerUPP(BOINCListBodyAccessibilityEventHandler), sizeof(myAccessibilityEvents) / sizeof(EventTypeSpec), myAccessibilityEvents, &accessibilityHandlerData, &m_pBodyAccessibilityEventHandlerRef); #endif } void CDlgEventLogListCtrl::RemoveMacAccessibilitySupport() { #if !USE_NATIVE_LISTCONTROL ::RemoveEventHandler(m_pHeaderAccessibilityEventHandlerRef); ::RemoveEventHandler(m_pBodyAccessibilityEventHandlerRef); #endif } void CProjectListCtrlAccessible::SetupMacAccessibilitySupport() { OSErr err; CProjectListCtrl* pCtrl = wxDynamicCast(mp_win, CProjectListCtrl); wxASSERT(pCtrl); if (pCtrl) { m_listView = (HIViewRef)pCtrl->GetHandle(); err = HIViewSetEnabled(m_listView, true); err = InstallHIObjectEventHandler((HIObjectRef)m_listView, NewEventHandlerUPP(AttachListAccessibilityEventHandler), sizeof(myAccessibilityEvents) / sizeof(EventTypeSpec), myAccessibilityEvents, this, &m_plistAccessibilityEventHandlerRef); } else { m_plistAccessibilityEventHandlerRef = NULL; } } void CProjectListCtrlAccessible::RemoveMacAccessibilitySupport() { if (m_plistAccessibilityEventHandlerRef) { ::RemoveEventHandler(m_plistAccessibilityEventHandlerRef); m_plistAccessibilityEventHandlerRef = NULL; } } void CSimplePanel::SetupMacAccessibilitySupport() { OSStatus err; wxString str = _("for accessibility support, please select advanced from the view menu or type command shift a"); HIViewRef simple = (HIViewRef)GetHandle(); CFStringRef description = CFStringCreateWithCString(NULL, str.char_str(), kCFStringEncodingUTF8); // Have the screen reader tell user to switch to advanced view. HIObjectSetAuxiliaryAccessibilityAttribute((HIObjectRef)simple, 0, kAXDescriptionAttribute, description); CFRelease( description ); err = InstallHIObjectEventHandler((HIObjectRef)simple, NewEventHandlerUPP(SimpleAccessibilityEventHandler), sizeof(Simple_AccessibilityEvents) / sizeof(EventTypeSpec), Simple_AccessibilityEvents, this, &m_pSGAccessibilityEventHandlerRef); } void CSimplePanel::RemoveMacAccessibilitySupport() { if (m_pSGAccessibilityEventHandlerRef) { ::RemoveEventHandler(m_pSGAccessibilityEventHandlerRef); m_pSGAccessibilityEventHandlerRef = NULL; } } void CTaskItemGroup::SetupMacAccessibilitySupport() { OSStatus err; HIViewRef boxView = (HIViewRef)m_pStaticBox->GetHandle(); if (m_pTaskGroupAccessibilityEventHandlerRef == NULL) { err = InstallHIObjectEventHandler((HIObjectRef)boxView, NewEventHandlerUPP(SimpleAccessibilityEventHandler), sizeof(Simple_AccessibilityEvents) / sizeof(EventTypeSpec), Simple_AccessibilityEvents, this, &m_pTaskGroupAccessibilityEventHandlerRef); } } void CTaskItemGroup::RemoveMacAccessibilitySupport() { if (m_pTaskGroupAccessibilityEventHandlerRef) { ::RemoveEventHandler(m_pTaskGroupAccessibilityEventHandlerRef); m_pTaskGroupAccessibilityEventHandlerRef = NULL; } } void CViewStatistics::SetupMacAccessibilitySupport() { OSStatus err; HIViewRef paintPanelView = (HIViewRef)m_PaintStatistics->GetHandle(); wxString str = _("This panel contains graphs showing user totals for projects"); CFStringRef description = CFStringCreateWithCString(NULL, str.char_str(), kCFStringEncodingUTF8); // Have the screen reader tell user to switch to advanced view. HIObjectSetAuxiliaryAccessibilityAttribute((HIObjectRef)paintPanelView, 0, kAXDescriptionAttribute, description); CFRelease( description ); err = InstallHIObjectEventHandler((HIObjectRef)paintPanelView, NewEventHandlerUPP(SimpleAccessibilityEventHandler), sizeof(Simple_AccessibilityEvents) / sizeof(EventTypeSpec), Simple_AccessibilityEvents, this, &m_pStatisticsAccessibilityEventHandlerRef); } void CViewStatistics::RemoveMacAccessibilitySupport() { ::RemoveEventHandler(m_pStatisticsAccessibilityEventHandlerRef); } void wxPieCtrl::SetupMacAccessibilitySupport() { OSStatus err; HIViewRef pieControlView = (HIViewRef)GetHandle(); HIObjectSetAuxiliaryAccessibilityAttribute((HIObjectRef)pieControlView, 0, kAXDescriptionAttribute, CFSTR("")); err = InstallHIObjectEventHandler((HIObjectRef)pieControlView, NewEventHandlerUPP(PieCtrlAccessibilityEventHandler), sizeof(Simple_AccessibilityEvents) / sizeof(EventTypeSpec), Simple_AccessibilityEvents, this, &m_pPieCtrlAccessibilityEventHandlerRef); } void wxPieCtrl::RemoveMacAccessibilitySupport() { if (m_pPieCtrlAccessibilityEventHandlerRef) { ::RemoveEventHandler(m_pPieCtrlAccessibilityEventHandlerRef); m_pPieCtrlAccessibilityEventHandlerRef = NULL; } } OSStatus BOINCListAccessibilityEventHandler( EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void* pData, Boolean isHeader) { const UInt32 eventClass = GetEventClass(inEvent); const UInt32 eventKind = GetEventKind(inEvent); OSStatus err; wxGenericListCtrl* pList = ((struct ListAccessData*)pData)->pList; CBOINCBaseView* pView = ((struct ListAccessData*)pData)->pView; HIViewRef headerView = ((struct ListAccessData*)pData)->headerView; HIViewRef bodyView = ((struct ListAccessData*)pData)->bodyView; CDlgEventLog* pEventLog = ((struct ListAccessData*)pData)->pEventLog; if (eventClass != kEventClassAccessibility) { return eventNotHandledErr; } AXUIElementRef element; UInt64 inIdentifier = 0; UInt64 outIdentifier = 0; SInt32 row = 0; SInt32 col = 0; HIObjectRef obj = NULL; err = GetEventParameter (inEvent, kEventParamAccessibleObject, typeCFTypeRef, NULL, sizeof(typeCFTypeRef), NULL, &element); if (err) return err; AXUIElementGetIdentifier( element, &inIdentifier ); obj = AXUIElementGetHIObject(element); if (inIdentifier) { if (! isHeader) { row = (inIdentifier / MAX_LIST_COL) - 1 + pList->GetTopItem(); } col = (inIdentifier % MAX_LIST_COL) - 1; } switch (eventKind) { case kEventAccessibleGetChildAtPoint: { CFTypeRef child = NULL; HIPoint where; long theRow = wxNOT_FOUND; long ignored; int hitflags; // Only the whole view can be tested since the parts don't have sub-parts. if (inIdentifier != 0) { return noErr; } err = GetEventParameter (inEvent, kEventParamMouseLocation, typeHIPoint, NULL, sizeof(HIPoint), NULL, &where); if (err) return err; wxPoint p((int)where.x, (int)where.y); pList->ScreenToClient(&p.x, &p.y); // HitTest returns the column only on wxMSW int x = 0, n = pList->GetColumnCount(); for (col=0; colGetColumnWidth(col); if (p.x < x) break; } if (col >= n) { return noErr; } if (isHeader) { if ((p.y > 0) || (p.y < -pList->m_headerHeight)) { return noErr; } outIdentifier = col+1; } else { theRow = pList->HitTest(p, hitflags, &ignored); if (theRow == wxNOT_FOUND) { return noErr; } outIdentifier = ((theRow + 1 - pList->GetTopItem()) * MAX_LIST_COL) + col + 1; } child = AXUIElementCreateWithHIObjectAndIdentifier(obj, outIdentifier ); if (child == NULL) { return eventNotHandledErr; } err = SetEventParameter (inEvent, kEventParamAccessibleChild, typeCFTypeRef, sizeof(typeCFTypeRef), &child); if (err) { return eventNotHandledErr; } return noErr; } break; case kEventAccessibleGetFocusedChild: return noErr; break; case kEventAccessibleGetAllAttributeNames: { CFMutableArrayRef namesArray; err = GetEventParameter (inEvent, kEventParamAccessibleAttributeNames, typeCFMutableArrayRef, NULL, sizeof(typeCFMutableArrayRef), NULL, &namesArray); if (err) return err; CallNextEventHandler( inHandlerCallRef, inEvent ); if ( inIdentifier == 0 ) { // Identifier 0 means "the whole view". // Let accessibility know that this view has children and can // return a list of them. CFArrayAppendValue( namesArray, kAXChildrenAttribute ); } else { // Let accessibility know that this view's children can return description, // size, position, parent window, top level element and isFocused attributes. CFArrayAppendValue( namesArray, kAXWindowAttribute ); CFArrayAppendValue( namesArray, kAXTopLevelUIElementAttribute ); CFArrayAppendValue( namesArray, kAXSizeAttribute ); CFArrayAppendValue( namesArray, kAXPositionAttribute ); if (isHeader) { CFArrayAppendValue( namesArray, kAXTitleAttribute ); } CFArrayAppendValue( namesArray, kAXEnabledAttribute ); } CFArrayAppendValue( namesArray, kAXFocusedAttribute ); CFArrayAppendValue( namesArray, kAXRoleAttribute ); CFArrayAppendValue( namesArray, kAXRoleDescriptionAttribute ); CFArrayAppendValue( namesArray, kAXDescriptionAttribute ); CFArrayAppendValue( namesArray, kAXParentAttribute ); return noErr; } break; case kEventAccessibleGetAllParameterizedAttributeNames: { CFMutableArrayRef namesArray; err = GetEventParameter (inEvent, kEventParamAccessibleAttributeNames, typeCFMutableArrayRef, NULL, sizeof(typeCFMutableArrayRef), NULL, &namesArray); if (err) return err; return noErr; } break; case kEventAccessibleGetNamedAttribute: { CFStringRef attribute; err = GetEventParameter (inEvent, kEventParamAccessibleAttributeName, typeCFStringRef, NULL, sizeof(typeCFStringRef), NULL, &attribute); if (err) return err; if ( CFStringCompare( attribute, kAXFocusedAttribute, 0 ) == kCFCompareEqualTo ) { // Return whether or not this part is focused. //TODO: Add kAXFocusedAttribute support? Boolean focused = false; SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeBoolean, sizeof( focused ), &focused ); return noErr; } if ( inIdentifier == 0 ) { // String compare the incoming attribute name and return the appropriate accessibility // information as an event parameter. if ( CFStringCompare( attribute, kAXChildrenAttribute, 0 ) == kCFCompareEqualTo ) { // Create and return an array of AXUIElements describing the children of this view. CFMutableArrayRef children; AXUIElementRef child; int c, n = pList->GetColumnCount(); if (isHeader) { children = CFArrayCreateMutable( kCFAllocatorDefault, n, &kCFTypeArrayCallBacks ); for ( c = 0; c < n; c++ ) { // Header item for each column outIdentifier = c+1; child = AXUIElementCreateWithHIObjectAndIdentifier( obj, outIdentifier ); CFArrayAppendValue( children, child ); CFRelease( child ); } } else { // ! isHeader int r, m = pList->GetCountPerPage(); children = CFArrayCreateMutable( kCFAllocatorDefault, (m + 1) * n, &kCFTypeArrayCallBacks ); for ( r = 0; r < m; r++ ) { // For each row for ( c = 0; c < n; c++ ) { // For each column outIdentifier = ((r + 1) * MAX_LIST_COL) + c + 1; child = AXUIElementCreateWithHIObjectAndIdentifier( obj, outIdentifier ); CFArrayAppendValue( children, child ); CFRelease( child ); } } } SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( children ), &children ); CFRelease( children ); return noErr; } else if ( CFStringCompare( attribute, kAXRoleAttribute, 0 ) == kCFCompareEqualTo ) { // Return a string indicating the role of this view. Using the table role doesn't work. CFStringRef role = isHeader? kAXColumnRole : kAXListRole; SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( role ), &role ); return noErr; } else if ( CFStringCompare( attribute, kAXRoleDescriptionAttribute, 0 ) == kCFCompareEqualTo ) { // Return a string indicating the role of this part. CFStringRef roleDesc = CFSTR(""); SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( roleDesc ), &roleDesc ); CFRelease( roleDesc ); return noErr; } else if ( CFStringCompare( attribute, kAXDescriptionAttribute, 0 ) == kCFCompareEqualTo ) { // Return a string indicating the role of this part. wxString str; if (isHeader) { str = _("blank"); } else { str = _("list of "); if (pEventLog) { str += _("events"); } else { if (pView) { str += pView->GetViewDisplayName(); } } if (pList->GetItemCount() <= 0) { str += _(" is empty"); } } CFStringRef description = CFStringCreateWithCString(NULL, str.char_str(), kCFStringEncodingUTF8); SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( description ), &description ); CFRelease( description ); return noErr; } else if ( CFStringCompare( attribute, kAXParentAttribute, 0 ) == kCFCompareEqualTo ) { AXUIElementRef parent; HIViewRef parentView; parentView = HIViewGetSuperview(isHeader ? headerView : bodyView); parent = AXUIElementCreateWithHIObjectAndIdentifier((HIObjectRef)parentView, 0); if (parent == NULL) { return eventNotHandledErr; } SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( parent ), &parent ); CFRelease( parent ); return noErr; } else if ( CFStringCompare( attribute, kAXSizeAttribute, 0 ) == kCFCompareEqualTo ) { HIRect r; err = HIViewGetBounds(isHeader ? headerView : bodyView, &r); HISize size = r.size; SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeHISize, sizeof( HISize ), &size ); return noErr; } else if ( CFStringCompare( attribute, kAXPositionAttribute, 0 ) == kCFCompareEqualTo ) { HIRect r; HIPoint pt; err = HIViewGetBounds(isHeader ? headerView : bodyView, &r); int x = r.origin.x, y = r.origin.y; // Now convert to global coordinates pList->ClientToScreen(&x, &y); pt.x = x; if (isHeader) { pt.y = y - pList->m_headerHeight; } else { pt.y = y; } SetEventParameter(inEvent, kEventParamAccessibleAttributeValue, typeHIPoint, sizeof(HIPoint), &pt); return noErr; } else { return CallNextEventHandler( inHandlerCallRef, inEvent ); } } else { // End if ( inIdentifier == 0 ) if ( CFStringCompare( attribute, kAXDescriptionAttribute, 0 ) == kCFCompareEqualTo ) { wxString str, buf; int rowCount; Boolean isCurrentSortCol = false; if (isHeader) { wxListItem headerItem; CBOINCBaseFrame* pFrame = wxGetApp().GetFrame(); if (!pFrame) { return eventNotHandledErr; } pList->GetColumn(col, headerItem); buf.Printf(_("%d of %d; "), col+1, pList->GetColumnCount()); if (pView) { if (col == pView->m_iSortColumn) { isCurrentSortCol = true; } } if (isCurrentSortCol) { str = _("current sort column "); str += buf; str += (pView->m_bReverseSort ? _(" descending order ") : _(" ascending order ")); } else { str = _("column "); str += buf; } str += headerItem.GetText(); } else { // ! isHeader rowCount = pList->GetItemCount(); if (rowCount <= 0) { str = _("list is empty"); } else { if (pList->GetItemState(row, wxLIST_STATE_SELECTED) & wxLIST_STATE_SELECTED) { str = _("selected "); } else { str = wxEmptyString; } buf.Printf(_("row %d "), row+1); str += buf; if (col == 0) { buf.Printf(_("of %d; "), rowCount); str += buf; } buf.Printf(_("column %d; "), col+1); str += buf; if (pEventLog) { buf = pEventLog->OnListGetItemText(row, col); } else { buf = pView->FireOnListGetItemText(row, col); } if (buf.IsEmpty()) { buf = _("blank"); } str += buf; } } CFStringRef description = CFStringCreateWithCString(NULL, str.char_str(), kCFStringEncodingUTF8); SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( description ), &description ); CFRelease( description ); return noErr; } else if ( CFStringCompare( attribute, kAXParentAttribute, 0 ) == kCFCompareEqualTo ) { AXUIElementRef parent; HIViewRef parentView; parentView = isHeader ? headerView : bodyView; parent = AXUIElementCreateWithHIObjectAndIdentifier((HIObjectRef)parentView, 0); if (parent == NULL) { return eventNotHandledErr; } SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( parent ), &parent ); CFRelease( parent ); return noErr; } else if ( CFStringCompare( attribute, kAXSubroleAttribute, 0 ) == kCFCompareEqualTo ) { CFStringRef subRole; int currentTabView; if (! isHeader) { return eventNotHandledErr; } CBOINCBaseFrame* pFrame = wxGetApp().GetFrame(); if (!pFrame) { return eventNotHandledErr; } currentTabView = pFrame->GetCurrentViewPage(); if (currentTabView & (VW_PROJ | VW_TASK |VW_XFER)) { subRole = kAXSortButtonSubrole; } else { return eventNotHandledErr; } SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( subRole ), &subRole ); return noErr; } else if ( CFStringCompare( attribute, kAXRoleAttribute, 0 ) == kCFCompareEqualTo ) { // Return a string indicating the role of this part. The parts of the view behave like // buttons, so use that system role. CFStringRef role; int currentTabView; if (isHeader) { CBOINCBaseFrame* pFrame = wxGetApp().GetFrame(); if (!pFrame) { return eventNotHandledErr; } currentTabView = pFrame->GetCurrentViewPage(); if (currentTabView & (VW_PROJ | VW_TASK |VW_XFER)) { role = kAXButtonRole; } else { role = kAXColumnRole; } } else { // ! isHeader role = kAXStaticTextRole; } SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( role ), &role ); return noErr; } else if ( CFStringCompare( attribute, kAXRoleDescriptionAttribute, 0 ) == kCFCompareEqualTo ) { // Return a string describing the role of this part. Use the system description. CFStringRef roleDesc; int currentTabView; if (isHeader) { CBOINCBaseFrame* pFrame = wxGetApp().GetFrame(); if (!pFrame) { return eventNotHandledErr; } currentTabView = pFrame->GetCurrentViewPage(); if (currentTabView & (VW_PROJ | VW_TASK |VW_XFER)) { roleDesc = HICopyAccessibilityRoleDescription( kAXButtonRole, kAXSortButtonSubrole ); } else { roleDesc = CFStringCreateCopy(NULL, CFSTR("")); } } else { // ! isHeader roleDesc = HICopyAccessibilityRoleDescription( kAXStaticTextRole, NULL ); } SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( roleDesc ), &roleDesc ); CFRelease( roleDesc ); return noErr; } else if ( CFStringCompare( attribute, kAXSizeAttribute, 0 ) == kCFCompareEqualTo ) { HISize size; wxRect r; // Return the size of this part as an HISize. size.width = pList->GetColumnWidth(col); if (isHeader) { size.height = pList->m_headerHeight; } else { // ! isHeader pList->GetItemRect(row, r); size.height = r.height; } SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeHISize, sizeof( HISize ), &size ); return noErr; } else if ( CFStringCompare( attribute, kAXPositionAttribute, 0 ) == kCFCompareEqualTo ) { HIPoint pt; wxRect r; int i, x = 0, y = 0; // Return the position of this part as an HIPoint. // First get the position relative to the ListCtrl for (i=0; i< col; i++) { x += pList->GetColumnWidth(i); } if (!isHeader) { pList->GetItemRect(row, r); y = r.y - 1; } // Now convert to global coordinates pList->ClientToScreen(&x, &y); pt.x = x; pt.y = y - pList->m_headerHeight; SetEventParameter(inEvent, kEventParamAccessibleAttributeValue, typeHIPoint, sizeof(HIPoint), &pt); return noErr; } else if ( CFStringCompare( attribute, kAXWindowAttribute, 0 ) == kCFCompareEqualTo || CFStringCompare( attribute, kAXTopLevelUIElementAttribute, 0 ) == kCFCompareEqualTo ) { // Return the window or top level ui element for this part. They are both the same so re-use the code. AXUIElementRef windOrTopUI; WindowRef win = GetControlOwner(bodyView); if (win == NULL) { return eventNotHandledErr; } windOrTopUI = AXUIElementCreateWithHIObjectAndIdentifier( (HIObjectRef)win, 0 ); if (windOrTopUI == NULL) { return eventNotHandledErr; } SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( windOrTopUI ), &windOrTopUI ); CFRelease( windOrTopUI ); return noErr; } else if ( CFStringCompare( attribute, kAXEnabledAttribute, 0 ) == kCFCompareEqualTo ) { Boolean enabled = true; SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeBoolean, sizeof( enabled ), &enabled ); return noErr; #if 0 } else if ( CFStringCompare( attribute, kAXFocusedAttribute, 0 ) == kCFCompareEqualTo ) { // Return whether or not this part is focused. //TODO: Add kAXFocusedAttribute support? Boolean focused = false; SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeBoolean, sizeof( focused ), &focused ); return noErr; #endif } else if ( CFStringCompare( attribute, kAXTitleAttribute, 0 ) == kCFCompareEqualTo ) { // Return the item's text wxString str; wxListItem headerItem; if (!isHeader) { return eventNotHandledErr; } pList->GetColumn(col, headerItem); str = headerItem.GetText(); CFStringRef title = CFStringCreateWithCString(NULL, str.char_str(), kCFStringEncodingUTF8); SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( title ), &title ); CFRelease( title ); return noErr; } else { return eventNotHandledErr; } } // End if ( inIdentifier != 0 ) break; } // End case kEventAccessibleGetNamedAttribute: case kEventAccessibleIsNamedAttributeSettable: { CFStringRef attribute; Boolean isSettable = false; err = GetEventParameter (inEvent, kEventParamAccessibleAttributeName, typeCFStringRef, NULL, sizeof(typeCFStringRef), NULL, &attribute); if (err) return err; // The focused attribute is the only settable attribute for this view, // and it can only be set on part (or subelements), not the whole view. if (inIdentifier != 0) { if ( CFStringCompare( attribute, kAXFocusedAttribute, 0 ) == kCFCompareEqualTo ) { isSettable = true; } } SetEventParameter( inEvent, kEventParamAccessibleAttributeSettable, typeBoolean, sizeof( Boolean ), &isSettable ); return noErr; } break; case kEventAccessibleSetNamedAttribute: { return eventNotHandledErr; } break; case kEventAccessibleGetAllActionNames: { CFMutableArrayRef array; err = GetEventParameter (inEvent, kEventParamAccessibleActionNames, typeCFMutableArrayRef, NULL, sizeof(typeCFMutableArrayRef), NULL, &array); if (err) return err; if (inIdentifier != 0) { CFArrayAppendValue( array, kAXPressAction ); } return noErr; } break; case kEventAccessibleGetNamedActionDescription: { CFStringRef action; CFMutableStringRef desc; CFStringRef selfDesc = NULL; if (inIdentifier == 0) { return eventNotHandledErr; } err = GetEventParameter (inEvent, kEventParamAccessibleActionName, typeCFStringRef, NULL, sizeof(typeCFStringRef), NULL, &action); if (err) return err; err = GetEventParameter (inEvent, kEventParamAccessibleActionDescription, typeCFMutableStringRef, NULL, sizeof(typeCFMutableStringRef), NULL, &desc); if (err) return err; selfDesc = HICopyAccessibilityActionDescription( action ); CFStringReplaceAll( desc, selfDesc ); CFRelease( selfDesc ); return noErr; } break; case kEventAccessiblePerformNamedAction: { CFStringRef action; wxWindowID id = pList->GetId(); if (inIdentifier == 0) { return eventNotHandledErr; } err = GetEventParameter (inEvent, kEventParamAccessibleActionName, typeCFStringRef, NULL, sizeof(typeCFStringRef), NULL, &action); if (err) return err; if ( CFStringCompare( action, kAXPressAction, 0 ) != kCFCompareEqualTo ) { return eventNotHandledErr; } if (isHeader) { wxListEvent event(wxEVT_COMMAND_LIST_COL_CLICK, id); event.m_col = col; pList->AddPendingEvent(event); } else { if (pView) { pView->ClearSelections(); } pList->SetItemState(row, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED); } return noErr; } break; default: return eventNotHandledErr; } // End switch(eventKind) return eventNotHandledErr; } pascal OSStatus AttachListAccessibilityEventHandler( EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void* pData) { const UInt32 eventClass = GetEventClass(inEvent); const UInt32 eventKind = GetEventKind(inEvent); OSStatus err; CProjectListCtrlAccessible* pAccessible = (CProjectListCtrlAccessible*)pData; if (pAccessible == NULL) { return eventNotHandledErr; } CProjectListCtrl* pCtrl = wxDynamicCast(pAccessible->GetWindow(), CProjectListCtrl); if (pCtrl == NULL) { return eventNotHandledErr; } if (eventClass != kEventClassAccessibility) { return eventNotHandledErr; } AXUIElementRef element; UInt64 inIdentifier = 0; UInt64 outIdentifier = 0; SInt32 row = 0; HIObjectRef obj = NULL; err = GetEventParameter (inEvent, kEventParamAccessibleObject, typeCFTypeRef, NULL, sizeof(typeCFTypeRef), NULL, &element); if (err) return err; AXUIElementGetIdentifier( element, &inIdentifier ); obj = AXUIElementGetHIObject(element); row = inIdentifier; switch (eventKind) { case kEventAccessibleGetChildAtPoint: { CFTypeRef child = NULL; HIPoint where; int hitRow; // Only the whole view can be tested since the parts don't have sub-parts. if (inIdentifier != 0) { return noErr; } err = GetEventParameter (inEvent, kEventParamMouseLocation, typeHIPoint, NULL, sizeof(HIPoint), NULL, &where); if (err) return err; wxPoint p((int)where.x, (int)where.y); pCtrl->ScreenToClient(&p.x, &p.y); err = pAccessible->HitTest(p, &hitRow, NULL); if (err) { return eventNotHandledErr; } if (hitRow >= 0) { //TODO: Check with Rom why this starts at 0 not 1 outIdentifier = hitRow + 1; child = AXUIElementCreateWithHIObjectAndIdentifier(obj, outIdentifier ); if (child == NULL) { return eventNotHandledErr; } err = SetEventParameter (inEvent, kEventParamAccessibleChild, typeCFTypeRef, sizeof(typeCFTypeRef), &child); if (err) { return eventNotHandledErr; } } return noErr; } break; case kEventAccessibleGetFocusedChild: return noErr; break; case kEventAccessibleGetAllAttributeNames: { CFMutableArrayRef namesArray; err = GetEventParameter (inEvent, kEventParamAccessibleAttributeNames, typeCFMutableArrayRef, NULL, sizeof(typeCFMutableArrayRef), NULL, &namesArray); if (err) return err; CallNextEventHandler( inHandlerCallRef, inEvent ); if ( inIdentifier == 0 ) { // Identifier 0 means "the whole view". // Let accessibility know that this view has children and can // return a list of them. CFArrayAppendValue( namesArray, kAXChildrenAttribute ); } else { // Let accessibility know that this view's children can return description, // size, position, parent window, top level element and isFocused attributes. CFArrayAppendValue( namesArray, kAXWindowAttribute ); CFArrayAppendValue( namesArray, kAXTopLevelUIElementAttribute ); CFArrayAppendValue( namesArray, kAXDescriptionAttribute ); CFArrayAppendValue( namesArray, kAXSizeAttribute ); CFArrayAppendValue( namesArray, kAXPositionAttribute ); CFArrayAppendValue( namesArray, kAXTitleAttribute ); CFArrayAppendValue( namesArray, kAXEnabledAttribute ); } CFArrayAppendValue( namesArray, kAXFocusedAttribute ); CFArrayAppendValue( namesArray, kAXRoleAttribute ); CFArrayAppendValue( namesArray, kAXRoleDescriptionAttribute ); CFArrayAppendValue( namesArray, kAXParentAttribute ); return noErr; } break; case kEventAccessibleGetAllParameterizedAttributeNames: { CFMutableArrayRef namesArray; err = GetEventParameter (inEvent, kEventParamAccessibleAttributeNames, typeCFMutableArrayRef, NULL, sizeof(typeCFMutableArrayRef), NULL, &namesArray); if (err) return err; return noErr; } break; case kEventAccessibleGetNamedAttribute: { CFStringRef attribute; err = GetEventParameter (inEvent, kEventParamAccessibleAttributeName, typeCFStringRef, NULL, sizeof(typeCFStringRef), NULL, &attribute); if (err) return err; if ( CFStringCompare( attribute, kAXFocusedAttribute, 0 ) == kCFCompareEqualTo ) { // Return whether or not this part is focused. //TODO: Add kAXFocusedAttribute support? Boolean focused = false; SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeBoolean, sizeof( focused ), &focused ); return noErr; } else if ( CFStringCompare( attribute, kAXSizeAttribute, 0 ) == kCFCompareEqualTo ) { wxRect r; HISize theSize; err = pAccessible->GetLocation(r, row); if (err) { return eventNotHandledErr; } theSize.width = r.width; theSize.height = r.height; SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeHISize, sizeof( HISize ), &theSize ); return noErr; } else if ( CFStringCompare( attribute, kAXPositionAttribute, 0 ) == kCFCompareEqualTo ) { wxRect r; HIPoint pt; int x, y; err = pAccessible->GetLocation(r, row); if (err) { return eventNotHandledErr; } x = r.x; y = r.y; // Now convert to global coordinates pCtrl->ClientToScreen(&x, &y); pt.x = x; pt.y = y; SetEventParameter(inEvent, kEventParamAccessibleAttributeValue, typeHIPoint, sizeof(HIPoint), &pt); return noErr; } if ( inIdentifier == 0 ) { // String compare the incoming attribute name and return the appropriate accessibility // information as an event parameter. if ( CFStringCompare( attribute, kAXChildrenAttribute, 0 ) == kCFCompareEqualTo ) { // Create and return an array of AXUIElements describing the children of this view. CFMutableArrayRef children; AXUIElementRef child; int i, n; err = pAccessible->GetChildCount(&n); children = CFArrayCreateMutable( kCFAllocatorDefault, n, &kCFTypeArrayCallBacks ); for ( i = 0; i < n; i++ ) { outIdentifier = i+1; child = AXUIElementCreateWithHIObjectAndIdentifier( obj, outIdentifier ); CFArrayAppendValue( children, child ); CFRelease( child ); } SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( children ), &children ); CFRelease( children ); return noErr; } else if ( CFStringCompare( attribute, kAXRoleAttribute, 0 ) == kCFCompareEqualTo ) { // Return a string indicating the role of this view. Using the table role doesn't work. CFStringRef role = kAXListRole; SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( role ), &role ); return noErr; } else if ( CFStringCompare( attribute, kAXRoleDescriptionAttribute, 0 ) == kCFCompareEqualTo ) { // Return a string indicating the role of this part. //TODO: specify whether projects or account managers wxString str; str = _("list of projects or account managers"); // str += pView->GetViewDisplayName(); CFStringRef roleDesc = CFStringCreateWithCString(NULL, str.char_str(), kCFStringEncodingUTF8); SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( roleDesc ), &roleDesc ); CFRelease( roleDesc ); return noErr; } else if ( CFStringCompare( attribute, kAXParentAttribute, 0 ) == kCFCompareEqualTo ) { AXUIElementRef parent; HIViewRef parentView; parentView = HIViewGetSuperview(pAccessible->m_listView); parent = AXUIElementCreateWithHIObjectAndIdentifier((HIObjectRef)parentView, 0); if (parent == NULL) { return eventNotHandledErr; } SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( parent ), &parent ); CFRelease( parent ); return noErr; } else { return CallNextEventHandler( inHandlerCallRef, inEvent ); } } else { // End if ( inIdentifier == 0 ) if ( CFStringCompare( attribute, kAXDescriptionAttribute, 0 ) == kCFCompareEqualTo ) { wxString str, buf; int n; err = pAccessible->GetChildCount(&n); if (err) { return eventNotHandledErr; } if (pCtrl->IsSelected(row - 1)) { str = _("selected "); } else { str = wxEmptyString; } buf.Printf(_("row %d of %d; "), row, n); str += buf; err = pAccessible->GetDescription(row, &buf); if (err) { return eventNotHandledErr; } str += buf; CFStringRef description = CFStringCreateWithCString(NULL, str.char_str(), kCFStringEncodingUTF8); SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( description ), &description ); CFRelease( description ); return noErr; } else if ( CFStringCompare( attribute, kAXParentAttribute, 0 ) == kCFCompareEqualTo ) { AXUIElementRef parent; HIViewRef parentView; parentView = pAccessible->m_listView; parent = AXUIElementCreateWithHIObjectAndIdentifier((HIObjectRef)parentView, 0); if (parent == NULL) { return eventNotHandledErr; } SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( parent ), &parent ); CFRelease( parent ); return noErr; } else if ( CFStringCompare( attribute, kAXSubroleAttribute, 0 ) == kCFCompareEqualTo ) { return eventNotHandledErr; } else if ( CFStringCompare( attribute, kAXRoleAttribute, 0 ) == kCFCompareEqualTo ) { // Return a string indicating the role of this part. The parts of the view behave like // buttons, so use that system role. CFStringRef role = kAXStaticTextRole; // kAXRowRole; SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( role ), &role ); return noErr; } else if ( CFStringCompare( attribute, kAXRoleDescriptionAttribute, 0 ) == kCFCompareEqualTo ) { // Return a string describing the role of this part. Use the system description. CFStringRef roleDesc = HICopyAccessibilityRoleDescription( kAXRowRole, NULL ); // CFStringRef roleDesc = HICopyAccessibilityRoleDescription( kAXStaticTextRole, NULL ); SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( roleDesc ), &roleDesc ); CFRelease( roleDesc ); return noErr; } else if ( CFStringCompare( attribute, kAXWindowAttribute, 0 ) == kCFCompareEqualTo || CFStringCompare( attribute, kAXTopLevelUIElementAttribute, 0 ) == kCFCompareEqualTo ) { // Return the window or top level ui element for this part. They are both the same so re-use the code. AXUIElementRef windOrTopUI; WindowRef win = GetControlOwner((HIViewRef)obj); if (win == NULL) { return eventNotHandledErr; } windOrTopUI = AXUIElementCreateWithHIObjectAndIdentifier( (HIObjectRef)win, 0 ); if (windOrTopUI == NULL) { return eventNotHandledErr; } SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( windOrTopUI ), &windOrTopUI ); CFRelease( windOrTopUI ); return noErr; } else if ( CFStringCompare( attribute, kAXEnabledAttribute, 0 ) == kCFCompareEqualTo ) { Boolean enabled = true; SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeBoolean, sizeof( enabled ), &enabled ); return noErr; #if 0 } else if ( CFStringCompare( attribute, kAXFocusedAttribute, 0 ) == kCFCompareEqualTo ) { // Return whether or not this part is focused. //TODO: Add kAXFocusedAttribute support? Boolean focused = false; SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeBoolean, sizeof( focused ), &focused ); return noErr; #endif } else if ( CFStringCompare( attribute, kAXTitleAttribute, 0 ) == kCFCompareEqualTo ) { // Return the item's text wxString str; wxListItem headerItem; pAccessible->GetName(row, &str); CFStringRef title = CFStringCreateWithCString(NULL, str.char_str(), kCFStringEncodingUTF8); SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( title ), &title ); CFRelease( title ); return noErr; } else { return eventNotHandledErr; } } // End if ( inIdentifier != 0 ) break; } // End case kEventAccessibleGetNamedAttribute: case kEventAccessibleIsNamedAttributeSettable: { CFStringRef attribute; Boolean isSettable = false; err = GetEventParameter (inEvent, kEventParamAccessibleAttributeName, typeCFStringRef, NULL, sizeof(typeCFStringRef), NULL, &attribute); if (err) return err; // The focused attribute is the only settable attribute for this view, // and it can only be set on part (or subelements), not the whole view. if (inIdentifier != 0) { if ( CFStringCompare( attribute, kAXFocusedAttribute, 0 ) == kCFCompareEqualTo ) { isSettable = true; } } SetEventParameter( inEvent, kEventParamAccessibleAttributeSettable, typeBoolean, sizeof( Boolean ), &isSettable ); return noErr; } break; case kEventAccessibleSetNamedAttribute: { return eventNotHandledErr; } break; case kEventAccessibleGetAllActionNames: { CFMutableArrayRef array; err = GetEventParameter (inEvent, kEventParamAccessibleActionNames, typeCFMutableArrayRef, NULL, sizeof(typeCFMutableArrayRef), NULL, &array); if (err) return err; if (inIdentifier != 0) { CFArrayAppendValue( array, kAXPressAction ); } return noErr; } break; case kEventAccessibleGetNamedActionDescription: { CFStringRef action; CFMutableStringRef desc; CFStringRef selfDesc = NULL; if (inIdentifier == 0) { return eventNotHandledErr; } err = GetEventParameter (inEvent, kEventParamAccessibleActionName, typeCFStringRef, NULL, sizeof(typeCFStringRef), NULL, &action); if (err) return err; err = GetEventParameter (inEvent, kEventParamAccessibleActionDescription, typeCFMutableStringRef, NULL, sizeof(typeCFMutableStringRef), NULL, &desc); if (err) return err; selfDesc = HICopyAccessibilityActionDescription( action ); CFStringReplaceAll( desc, selfDesc ); CFRelease( selfDesc ); return noErr; } break; case kEventAccessiblePerformNamedAction: { CFStringRef action; if (inIdentifier == 0) { return eventNotHandledErr; } err = GetEventParameter (inEvent, kEventParamAccessibleActionName, typeCFStringRef, NULL, sizeof(typeCFStringRef), NULL, &action); if (err) return err; if ( CFStringCompare( action, kAXPressAction, 0 ) != kCFCompareEqualTo ) { return eventNotHandledErr; } err = pAccessible->DoDefaultAction(inIdentifier); if (err) { return eventNotHandledErr; } return noErr; } break; default: return eventNotHandledErr; } // End switch(eventKind) return eventNotHandledErr; } pascal OSStatus SimpleAccessibilityEventHandler( EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void* pData) { const UInt32 eventClass = GetEventClass(inEvent); const UInt32 eventKind = GetEventKind(inEvent); OSStatus err; if (eventClass != kEventClassAccessibility) { return eventNotHandledErr; } switch (eventKind) { case kEventAccessibleGetNamedAttribute: CFStringRef attribute; err = GetEventParameter (inEvent, kEventParamAccessibleAttributeName, typeCFStringRef, NULL, sizeof(typeCFStringRef), NULL, &attribute); if (err) return err; if ( CFStringCompare( attribute, kAXRoleAttribute, 0 ) == kCFCompareEqualTo ) { CFStringRef role = kAXStaticTextRole; SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( role ), &role ); return noErr; } return CallNextEventHandler( inHandlerCallRef, inEvent ); return eventNotHandledErr; break; default: return CallNextEventHandler( inHandlerCallRef, inEvent ); return eventNotHandledErr; } return eventNotHandledErr; } pascal OSStatus PieCtrlAccessibilityEventHandler( EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void* pData) { const UInt32 eventClass = GetEventClass(inEvent); const UInt32 eventKind = GetEventKind(inEvent); OSStatus err; wxPieCtrl* pPieCtrl = (wxPieCtrl*)pData; if (eventClass != kEventClassAccessibility) { return eventNotHandledErr; } switch (eventKind) { case kEventAccessibleGetNamedAttribute: CFStringRef attribute; err = GetEventParameter (inEvent, kEventParamAccessibleAttributeName, typeCFStringRef, NULL, sizeof(typeCFStringRef), NULL, &attribute); if (err) return err; if ( CFStringCompare( attribute, kAXRoleAttribute, 0 ) == kCFCompareEqualTo ) { CFStringRef role = kAXStaticTextRole; SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( role ), &role ); return noErr; } else if ( CFStringCompare( attribute, kAXDescriptionAttribute, 0 ) == kCFCompareEqualTo ) { // Return a string indicating the role of this part. wxString str; CFStringRef description; unsigned int i; str = pPieCtrl->GetLabel(); for(i=0; im_Series.Count(); i++) { str += wxT("; "); str += pPieCtrl->m_Series[i].GetLabel(); } description = CFStringCreateWithCString(NULL, str.char_str(), kCFStringEncodingUTF8); SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( description ), &description ); CFRelease( description ); return noErr; } return CallNextEventHandler( inHandlerCallRef, inEvent ); return eventNotHandledErr; break; default: return CallNextEventHandler( inHandlerCallRef, inEvent ); return eventNotHandledErr; } return eventNotHandledErr; }